Skip to content

g1 region Adaptive_young_list_length

wangkaiwu edited this page Jan 11, 2022 · 3 revisions

adaptive_young_list_length

  1. 该代码用于设置是否动态调整年轻代的大小,默认情况下是 true.以下情况会设置为 false。
    • 当设置了参数 NewRatio 参数,且生效
    • 设置了 NewSize 和 MaxNewSize 两个参数,且两个参数值一样时。
  2. 在不设参数的情况洗,默认是自适应调整年轻代大小,范围如下:
    • 最小值(同时也是初始大小)是 heap 当前已激活的 region 个数的 5%
    • 最大值是 heap 当前已激活的 region 个数的 60%

相关代码

class G1YoungGenSizer {
    bool adaptive_young_list_length() const {
        return _adaptive_size;
    }
};

赋值过程

过程
G1YoungGenSizer::G1YoungGenSizer() : _sizer_kind(SizerDefaults), 
        // _adaptive_size 的默认值是 true,在 NewRatio 参数生效之后会设为 false
        _adaptive_size(true),
        _min_desired_young_length(0), _max_desired_young_length(0) {
    // 参数 NewRatio 是否进行了设置, 默认值是 2
    if (FLAG_IS_CMDLINE(NewRatio)) {
        if (FLAG_IS_CMDLINE(NewSize) || FLAG_IS_CMDLINE(MaxNewSize)) {
            // 如果设置了 NewSize 或 MaxNewSize,则 NewRatio 会被覆盖
            log_warning(gc, ergo)("-XX:NewSize and -XX:MaxNewSize override -XX:NewRatio");
        } else {
            _sizer_kind = SizerNewRatio;
            // 由于限定了年轻代的比例,所以大小无法自适应
            // ————————————————————
            _adaptive_size = false;
            // ————————————————————
            return;
        }
    }
    if (FLAG_IS_CMDLINE(NewSize)) {
        // 如果设置了初始的年轻代大小
        // 计算一下应该初始分配多少个 region
        _min_desired_young_length = MAX2((uint) (NewSize / HeapRegion::GrainBytes), 1U);
        if (FLAG_IS_CMDLINE(MaxNewSize)) {
            // 如果设置了最大的年轻代大小
            // 计算一下最多有几个年轻代的 region
            _max_desired_young_length = MAX2((uint) (MaxNewSize / HeapRegion::GrainBytes), 1U);
            // 当前的策略是设置了最大和初始年轻代大小
            _sizer_kind = SizerMaxAndNewSize;
            // 只要初始值和最大值不一样,则就是年轻代自适应大小
            // —————————————————————————————————————————————————————————————————————
            _adaptive_size = _min_desired_young_length != _max_desired_young_length;
            // —————————————————————————————————————————————————————————————————————
        } else {
            // 仅设置了初始的年轻代大小
            _sizer_kind = SizerNewSizeOnly;
        }
    } else if (FLAG_IS_CMDLINE(MaxNewSize)) {
        // 计算一下最多有几个年轻代的 region
        _max_desired_young_length = MAX2((uint) (MaxNewSize / HeapRegion::GrainBytes), 1U);
        // 策略是仅设置了最大年轻代大小
        _sizer_kind = SizerMaxNewSizeOnly;
    }
}

关于自适应

当自适应启用时,且没有设置过 NewSize 或 MaxNewSize时,年轻代的初始 region 个数和最大 region 个数分别是多少呢?

相关代码

  1. 相关计算的代码如下
void G1YoungGenSizer::recalculate_min_max_young_length(uint number_of_heap_regions, uint* min_young_length, uint* max_young_length) {
  switch (_sizer_kind) {
    case SizerDefaults:
        // 默认策略,也就是没有设置任何参数
      *min_young_length = calculate_default_min_length(number_of_heap_regions);
      *max_young_length = calculate_default_max_length(number_of_heap_regions);
      break;
    case SizerNewSizeOnly:
        // 只设置了 NewSize,所以只需要计算最大个数
      *max_young_length = calculate_default_max_length(number_of_heap_regions);
      *max_young_length = MAX2(*min_young_length, *max_young_length);
      break;
    case SizerMaxNewSizeOnly:
        // 只设置了 MaxNewSize,所以只需要计算初始个数
      *min_young_length = calculate_default_min_length(number_of_heap_regions);
      *min_young_length = MIN2(*min_young_length, *max_young_length);
      break;
    case SizerMaxAndNewSize:
        // 最大和最小都设置了
      break;
    case SizerNewRatio:
        // 设置了 NewRatio 参数,所以最大最小值都是一样的
      *min_young_length = number_of_heap_regions / (NewRatio + 1);
      *max_young_length = *min_young_length;
      break;
  }
}
  1. 方法的第一个参数 number_of_heap_regions,从字面意思来看是当前 heap 的 region 个数,但还是要深究一下。下面先看下最小值和最大值的计算方法,最后看下 number_of_heap_regions 参数的来源.
最大值和最小值

初始 region 最小值

下面的方法是计算初始大小的方法,参数 G1NewSizePercent 的默认值是 5

uint G1YoungGenSizer::calculate_default_min_length(uint new_number_of_heap_regions) {
    // 当前总 region 个数的 5% 是年轻代的初始 region 个数
    uint default_value = (new_number_of_heap_regions * G1NewSizePercent) / 100;
    return MAX2(1U, default_value);
}

最大 region 个数

下面的方法是计算最大个数的方法,参数G1MaxNewSizePercent 的默认值是 60

uint G1YoungGenSizer::calculate_default_max_length(uint new_number_of_heap_regions) {
   // 当前 region 个数的 60% 是年轻代的最大 region 个数
   uint default_value = (new_number_of_heap_regions * G1MaxNewSizePercent) / 100;
   return MAX2(1U, default_value);
}
number_of_heap_regions 参数来源

number_of_heap_regions 参数来源

一共由两个地方调用

  1. adjust_max_new_size() 方法
void G1YoungGenSizer::adjust_max_new_size(uint number_of_heap_regions) {
   uint temp = _min_desired_young_length;
   uint result = _max_desired_young_length;
   recalculate_min_max_young_length(number_of_heap_regions, &temp, &result);
}
  1. heap_size_changed() 方法
void G1YoungGenSizer::heap_size_changed(uint new_number_of_heap_regions) {
  recalculate_min_max_young_length(new_number_of_heap_regions, &_min_desired_young_length, &_max_desired_young_length);
}

下面分别对这两个调用展开讨论

adjust_max_new_size() 方法

可以看到在这个调用链下,number_of_heap_regions 的值是整个 heap 的最大 region 个数。

void G1Policy::init(G1CollectedHeap* g1h, G1CollectionSet* collection_set) {
    _g1h = g1h;
    _young_gen_sizer.adjust_max_new_size(_g1h->max_regions());
}

heap_size_changed() 方法

以下代码是 heap_size_changed() 的内容,其中参数 new_number_of_regions 可以认为是当前已激活的 region 的个数.

void G1Policy::record_new_heap_size(uint new_number_of_regions) {
    // 根据当前已激活的 region 个数来计算年轻代的最小值和最大值
   _young_gen_sizer.heap_size_changed(new_number_of_regions);
}

上述方法的调用共有三处,过于复杂暂时不去解读三个调用的内容.

调用一
bool G1CollectedHeap::expand(size_t expand_bytes, WorkGang* pretouch_workers, double* expand_time_ms) {
    size_t aligned_expand_bytes = ReservedSpace::page_align_size_up(expand_bytes);
    aligned_expand_bytes = align_up(aligned_expand_bytes, HeapRegion::GrainBytes);
    uint regions_to_expand = (uint)(aligned_expand_bytes / HeapRegion::GrainBytes);
    uint expanded_by = _hrm.expand_by(regions_to_expand, pretouch_workers);
    if (expanded_by > 0) {
        g1_policy()->record_new_heap_size(num_regions());
    }
}
调用二
HeapWord* G1CollectedHeap::humongous_obj_allocate(size_t word_size) {
    uint obj_regions = (uint) humongous_obj_size_in_regions(word_size);
    if (first == G1_NO_HRM_INDEX) {
       first = _hrm.find_contiguous_empty_or_unavailable(obj_regions);
       if (first != G1_NO_HRM_INDEX) {
           g1_policy()->record_new_heap_size(num_regions());
       }
    }
}
调用三
void G1CollectedHeap::shrink_helper(size_t shrink_bytes) {
    uint num_regions_to_remove = (uint)(shrink_bytes / HeapRegion::GrainBytes);
    uint num_regions_removed = _hrm.shrink_by(num_regions_to_remove);
    if (num_regions_removed > 0) {
        g1_policy()->record_new_heap_size(num_regions());
    }
}

Clone this wiki locally