目录
2、构造方法 / initialize_all /post_heap_initialize
3、satisfy_failed_metadata_allocation
1、构造方法 /initialize_flags /initialize_size_info
在《Hotspot 垃圾回收之CollectedHeap 源码解析》中讲解了CollectedHeap的类继承关系,从本篇博客开始会依次讲解CollectedHeap的子类SharedHeap和GenCollectedHeap依赖的相关基础设施类的定义和实现,先把相关的关键基础类学习完成,最后再学习GenCollectedHeap的方法实现。因为某些类在不同GC算法下有不同的实现子类且子类的实现大多跟该GC算法本身的数据结构强耦合,为了方便理解,从本篇博客开始重点关注CMS算法用到的相关子类实现,先完整的学习CMS算法的实现再考虑其他算法下特定子类的实现。
一、CollectorPolicy
1、定义
CollectorPolicy的定义在hotspot/src/share/vm/memory/collectorPolicy.hpp中,该类及其子类用于定义垃圾回收器使用的全局属性,并初始化分代内存及其他共享资源。CollectorPolicy定义的属性如下:
- GCPolicyCounters* _gc_policy_counters; //跟踪分代内存的性能的计数器
- size_t _initial_heap_byte_size; //初始堆内存
- size_t _max_heap_byte_size; //最大堆内存
- size_t _min_heap_byte_size; //最小堆内存
- size_t _space_alignment; //space分配粒度
- size_t _heap_alignment; //heap分配粒度,_heap_alignment必须大于_space_alignment,且是_space_alignment的整数倍
- bool _max_heap_size_cmdline; //是否通过命令行参数设置了最大堆内存
- AdaptiveSizePolicy* _size_policy; //用来自适应调整堆内存大小的策略实现
- bool _should_clear_all_soft_refs; //是否需要清除所有的软引用,当软引用清除结束,垃圾回收器会将其置为false
- bool _all_soft_refs_clear; //当GC刚清除完所有的软引用时会设置该属性为true,当返回mutator时被设置成false
CollectorPolicy的类继承关系如下:
CollectorPolicy定义了一个枚举Name来描述子类的名称,如下:
通过kind方法可以获取当前子类的类型,如下:
CollectorPolicy定义的方法中除属性读写相关的,获取子类类型相关的方法是非虚方法外,大部分方法都是虚方法,重点关注其初始化方法的实现。
2、构造方法 / initialize_all /post_heap_initialize
这三个方法都是用来构造并初始化CollectorPolicy,构造方法调用后就会调用initialize_all方法,当CollectedHeap初始化结束后就会调用post_heap_initialize对已初始化的参数做必要的更新。initialize_all和post_heap_initialize的调用链如下:
post_heap_initialize方法没有给出默认实现,另外两方法的实现如下:
CollectorPolicy::CollectorPolicy() :
_space_alignment(0),
_heap_alignment(0),
_initial_heap_byte_size(InitialHeapSize), //InitialHeapSize表示初始堆内存,单位字节
_max_heap_byte_size(MaxHeapSize), //MaxHeapSize表示最大堆内存,单位字节
_min_heap_byte_size(Arguments::min_heap_size()),
_max_heap_size_cmdline(false),
_size_policy(NULL),
_should_clear_all_soft_refs(false),
_all_soft_refs_clear(false)
{}
virtual void initialize_all() {
//initialize_alignments用来初始化分代内存及内存分配相关属性的,没有默认实现
initialize_alignments();
//initialize_flags主要用于校验参数的合法性,并设置相关参数
initialize_flags();
initialize_size_info();
}
void CollectorPolicy::initialize_flags() {
//校验_space_alignment和_heap_alignment的合法性,_heap_alignment必须大于_space_alignment,且是_space_alignment的整数倍
assert(_space_alignment != 0, "Space alignment not set up properly");
assert(_heap_alignment != 0, "Heap alignment not set up properly");
assert(_heap_alignment >= _space_alignment,
err_msg("heap_alignment: " SIZE_FORMAT " less than space_alignment: " SIZE_FORMAT,
_heap_alignment, _space_alignment));
assert(_heap_alignment % _space_alignment == 0,
err_msg("heap_alignment: " SIZE_FORMAT " not aligned by space_alignment: " SIZE_FORMAT,
_heap_alignment, _space_alignment));
//如果MaxHeapSize是通过命令行参数配置的
if (FLAG_IS_CMDLINE(MaxHeapSize)) {
//校验MaxHeapSize和InitialHeapSize的合法性
if (FLAG_IS_CMDLINE(InitialHeapSize) && InitialHeapSize > MaxHeapSize) {
vm_exit_during_initialization("Initial heap size set to a larger value than the maximum heap size");
}
if (_min_heap_byte_size != 0 && MaxHeapSize < _min_heap_byte_size) {
vm_exit_during_initialization("Incompatible minimum and maximum heap sizes specified");
}
_max_heap_size_cmdline = true;
}
// M是一个常量,表示1M
if (InitialHeapSize < M) {
vm_exit_during_initialization("Too small initial heap");
}
if (_min_heap_byte_size < M) {
vm_exit_during_initialization("Too small minimum heap");
}
// User inputs from -Xmx and -Xms must be aligned
_min_heap_byte_size = align_size_up(_min_heap_byte_size, _heap_alignment);
//将内存对齐
uintx aligned_initial_heap_size = align_size_up(InitialHeapSize, _heap_alignment);
uintx aligned_max_heap_size = align_size_up(MaxHeapSize, _heap_alignment);
//如果原来的设置值没有对齐,则修改原来设置的值
if (aligned_initial_heap_size != InitialHeapSize) {
FLAG_SET_ERGO(uintx, InitialHeapSize, aligned_initial_heap_size);
}
if (aligned_max_heap_size != MaxHeapSize) {
FLAG_SET_ERGO(uintx, MaxHeapSize, aligned_max_heap_size);
}
//校验参数合法性,必要时修改
if (FLAG_IS_CMDLINE(InitialHeapSize) && _min_heap_byte_size != 0 &&
InitialHeapSize < _min_heap_byte_size) {
vm_exit_during_initialization("Incompatible minimum and initial heap sizes specified");
}
if (!FLAG_IS_DEFAULT(InitialHeapSize) && InitialHeapSize > MaxHeapSize) {
FLAG_SET_ERGO(uintx, MaxHeapSize, InitialHeapSize);
} else if (!FLAG_IS_DEFAULT(MaxHeapSize) && InitialHeapSize > MaxHeapSize) {
FLAG_SET_ERGO(uintx, InitialHeapSize, MaxHeapSize);
if (InitialHeapSize < _min_heap_byte_size) {
_min_heap_byte_size = InitialHeapSize;
}
}
_initial_heap_byte_size = InitialHeapSize;
_max_heap_byte_size = MaxHeapSize;
//MinHeapDeltaBytes参数表示堆内存空间因为GC而发生变动的最小值
FLAG_SET_ERGO(uintx, MinHeapDeltaBytes, align_size_up(MinHeapDeltaBytes, _space_alignment));
DEBUG_ONLY(CollectorPolicy::assert_flags();)
}
void CollectorPolicy::initialize_size_info() {
if (PrintGCDetails && Verbose) {
//打印日志
gclog_or_tty->print_cr("Minimum heap " SIZE_FORMAT " Initial heap "
SIZE_FORMAT " Maximum heap " SIZE_FORMAT,
_min_heap_byte_size, _initial_heap_byte_size, _max_heap_byte_size);
}
DEBUG_ONLY(CollectorPolicy::assert_size_info();)
}
#define FLAG_IS_CMDLINE(name) (CommandLineFlagsEx::is_cmdline(FLAG_MEMBER(name)))
3、satisfy_failed_metadata_allocation
satisfy_failed_metadata_allocation是当Metaspace分配内存失败后调用的,用来清理Metaspace空间并尝试重新分配的,该方法的调用链如下:
Metaspace::allocate中的调用如下图:
其源码实现如下:
MetaWord* CollectorPolicy::satisfy_failed_metadata_allocation(
ClassLoaderData* loader_data,
size_t word_size,
Metaspace::MetadataType mdtype) {
uint loop_count = 0;
uint gc_count = 0;
uint full_gc_count = 0;
//当前线程没有拥有Heap_lock
assert(!Heap_lock->owned_by_self(), "Should not be holding the Heap_lock");
do {
MetaWord* result = NULL;
//is_active表示有线程处在JNI关键区中,不能执行GC,needs_gc表示需要GC
if (GC_locker::is_active_and_needs_gc()) {
//尝试去扩展Metaspace并分配
result =
loader_data->metaspace_non_null()->expand_and_allocate(word_size,
mdtype);
if (result != NULL) {
//分配成功
return result;
}
JavaThread* jthr = JavaThread::current();
//如果当前线程不在JNI关键区中
if (!jthr->in_critical()) {
// 阻塞直到所有处在JNI关键区中的线程从JNI关键区中退出,最后一个从关键区退出的线程会在退出时触发GC,参考GC_Locker的实现
GC_locker::stall_until_clear();
//GC已经执行,重新循环尝试分配,GC执行完成后is_active_and_needs_gc会返回false
continue;
} else {
if (CheckJNICalls) {
fatal("Possible deadlock due to allocating while"
" in jni critical section");
}
//线程在关键区中不能GC,所以分配失败,返回NULL
return NULL;
}
}
//GC完成后下一次循环is_active_and_needs_gc返回false,就会进入此逻辑
{ //获取Heap_lock,读取GC的累计次数
MutexLocker ml(Heap_lock);
gc_count = Universe::heap()->total_collections();
full_gc_count = Universe::heap()->total_full_collections();
}
//如果之前有线程处于关键区中,则通过VM_CollectForMetadataAllocation会执行内存分配
//如果之前没有线程处于关键区中,则通过VM_CollectForMetadataAllocation触发GC并在GC完成后分配内存
//在VM_CollectForMetadataAllocation 执行的时候会判断是否需要跳过此次GC,因为退出关键区的线程已经触发了GC,但是有可能短期内又没内存了需要再次GC
VM_CollectForMetadataAllocation op(loader_data,
word_size,
mdtype,
gc_count,
full_gc_count,
GCCause::_metadata_GC_threshold);
//当前线程会被阻塞直到VM_CollectForMetadataAllocation被VMThread执行完成
VMThread::execute(&op);
//VM_CollectForMetadataAllocation已经执行完成,gc_locked为true说明此次GC后依然没有足够的内存,需要再次循环再次GC
if (op.gc_locked()) {
continue;
}
//gc_locked返回false,说明跳过了本次GC或者GC正常已完成内存分配
//prologue_succeeded返回false表示会跳过此次GC,VM_CollectForMetadataAllocation中的doit方法不会被执行,即不会分配内存,返回true表示会执行内存分配,分配失败再执行GC
if (op.prologue_succeeded()) {
return op.result();
}
//prologue_succeeded返回false继续循环
loop_count++;
//QueuedAllocationWarningCount表示循环了多少次数后打印warning日志,默认为o
if ((QueuedAllocationWarningCount > 0) &&
(loop_count % QueuedAllocationWarningCount == 0)) {
warning("satisfy_failed_metadata_allocation() retries %d times \n\t"
" size=" SIZE_FORMAT, loop_count, word_size);
}
} while (true); // Until a GC is done
}
二、 GCPolicyCounters
GCPolicyCounters的定义在hotspot/src/share/vm/gc_implementation/shared/gCPolicyCounters.hpp中,用来统计GC过程中各种性能数据。GCPolicyCounters的类继承关系如下:
GCPolicyCounters也定义了一个枚举Name来描述子类,如下:
GCPolicyCounters定义的属性只有三个,如下:
子类定义的属性就就很多了,而且是跟特定算法数据结构相关的属性,以GCAdaptivePolicyCounters为例,如下:
GCPolicyCounters及其子类定义的方法都是读写这些属性的,没有复杂逻辑。
三、AdaptiveSizePolicy
AdaptiveSizePolicy定义在hotspot/src/share/vm/gc_implementation/shared/adaptiveSizePolicy.hpp中,用于保存GC停顿和耗时等统计数据,并基于这些数据计算出各分代内存区的大小,从而实现动态的自适应调整各分代内存的大小,其类继承关系如下:
AdaptiveSizePolicy同样定义了一个描述子类的枚举GCPolicyKind,如下:
AdaptiveSizePolicy定义的属性基本都是各种维度的统计指标,比如GC耗时,GC间隔时间,GC停顿时间等,如下图:
具体指标的用途需要结合相关方法的调用来理解,这里暂且不做深入探讨。
四、GenCollectorPolicy
GenCollectorPolicy继承自CollectorPolicy,表示分代内存使用的CollectorPolicy,同样定义在collectorPolicy.hpp中,增加了如下属性:
- size_t _min_gen0_size; //gen0的内存最小值
- size_t _initial_gen0_size; //gen0的内存初始值
- size_t _max_gen0_size; //gen0的内存最大值
- size_t _gen_alignment; //分代内存分配粒度,_gen_alignment必须被_space_alignment整除,_heap_alignment被_gen_alignment整除
- GenerationSpec **_generations; //一种特殊的Generation实现
重点关注以下方法的实现。
1、构造方法 /initialize_flags /initialize_size_info
这三个都是GenCollectorPolicy的初始化方法,改写了父类的默认实现,除计算各种属性值外还需要初始化所有的Generation,如下:
GenCollectorPolicy::GenCollectorPolicy() :
_min_gen0_size(0),
_initial_gen0_size(0),
_max_gen0_size(0),
_gen_alignment(0),
_generations(NULL)
{}
virtual void initialize_generations() { };
virtual void initialize_all() {
CollectorPolicy::initialize_all();
initialize_generations();
}
void GenCollectorPolicy::initialize_flags() {
//调用父类方法初始化父类属性
CollectorPolicy::initialize_flags();
//校验_gen_alignment参数的合法性,_gen_alignment必须被_space_alignment整除,_heap_alignment被_gen_alignment整除
assert(_gen_alignment != 0, "Generation alignment not set up properly");
assert(_heap_alignment >= _gen_alignment,
err_msg("heap_alignment: " SIZE_FORMAT " less than gen_alignment: " SIZE_FORMAT,
_heap_alignment, _gen_alignment));
assert(_gen_alignment % _space_alignment == 0,
err_msg("gen_alignment: " SIZE_FORMAT " not aligned by space_alignment: " SIZE_FORMAT,
_gen_alignment, _space_alignment));
assert(_heap_alignment % _gen_alignment == 0,
err_msg("heap_alignment: " SIZE_FORMAT " not aligned by gen_alignment: " SIZE_FORMAT,
_heap_alignment, _gen_alignment));
// All generational heaps have a youngest gen; handle those flags here
// Make sure the heap is large enough for two generations
//young_gen_size_lower_bound方法返回年轻代的内存最小值
uintx smallest_new_size = young_gen_size_lower_bound();
//老年代只有一个区,将老年代的最小值加上年轻代的最小值,按照_heap_alignment向上取整就是堆内存的最小值
uintx smallest_heap_size = align_size_up(smallest_new_size + align_size_up(_space_alignment, _gen_alignment),
_heap_alignment);
//重置MaxHeapSize
if (MaxHeapSize < smallest_heap_size) {
FLAG_SET_ERGO(uintx, MaxHeapSize, smallest_heap_size);
_max_heap_byte_size = MaxHeapSize;
}
// If needed, synchronize _min_heap_byte size and _initial_heap_byte_size
if (_min_heap_byte_size < smallest_heap_size) {
_min_heap_byte_size = smallest_heap_size;
//重置InitialHeapSize
if (InitialHeapSize < _min_heap_byte_size) {
FLAG_SET_ERGO(uintx, InitialHeapSize, smallest_heap_size);
_initial_heap_byte_size = smallest_heap_size;
}
}
// NewSize表示一个新的generation(分代内存区)的初始内存值
smallest_new_size = MAX2(smallest_new_size, (uintx)align_size_down(NewSize, _gen_alignment));
if (smallest_new_size != NewSize) {
// 没有使用FLAG_SET_ERGO重置,因为该参数可能通过命令行被改写
NewSize = smallest_new_size;
}
_initial_gen0_size = NewSize;
//MaxNewSize表示表示一个新的generation(分代内存区)的最大内存值
//如果MaxNewSize不是默认值
if (!FLAG_IS_DEFAULT(MaxNewSize)) {
uintx min_new_size = MAX2(_gen_alignment, _min_gen0_size);
if (MaxNewSize >= MaxHeapSize) {
// Make sure there is room for an old generation
uintx smaller_max_new_size = MaxHeapSize - _gen_alignment;
if (FLAG_IS_CMDLINE(MaxNewSize)) {
warning("MaxNewSize (" SIZE_FORMAT "k) is equal to or greater than the entire "
"heap (" SIZE_FORMAT "k). A new max generation size of " SIZE_FORMAT "k will be used.",
MaxNewSize/K, MaxHeapSize/K, smaller_max_new_size/K);
}
//重置MaxNewSize
FLAG_SET_ERGO(uintx, MaxNewSize, smaller_max_new_size);
if (NewSize > MaxNewSize) {
//重置NewSize
FLAG_SET_ERGO(uintx, NewSize, MaxNewSize);
_initial_gen0_size = NewSize;
}
} else if (MaxNewSize < min_new_size) {
FLAG_SET_ERGO(uintx, MaxNewSize, min_new_size);
} else if (!is_size_aligned(MaxNewSize, _gen_alignment)) {
FLAG_SET_ERGO(uintx, MaxNewSize, align_size_down(MaxNewSize, _gen_alignment));
}
_max_gen0_size = MaxNewSize;
}
if (NewSize > MaxNewSize) {
// At this point this should only happen if the user specifies a large NewSize and/or
// a small (but not too small) MaxNewSize.
if (FLAG_IS_CMDLINE(MaxNewSize)) {
warning("NewSize (" SIZE_FORMAT "k) is greater than the MaxNewSize (" SIZE_FORMAT "k). "
"A new max generation size of " SIZE_FORMAT "k will be used.",
NewSize/K, MaxNewSize/K, NewSize/K);
}
//重置MaxNewSize
FLAG_SET_ERGO(uintx, MaxNewSize, NewSize);
_max_gen0_size = MaxNewSize;
}
//SurvivorRatio表示eden/survivor的倍数,默认是8
//NewRatio表示老年代与年轻代的内存空间比例,默认是2
if (SurvivorRatio < 1 || NewRatio < 1) {
vm_exit_during_initialization("Invalid young gen ratio specified");
}
DEBUG_ONLY(GenCollectorPolicy::assert_flags();)
}
//返回年轻代的内存最小值
size_t GenCollectorPolicy::young_gen_size_lower_bound() {
//年轻代包含三个区,eden+two survivors,所以这里是3
return align_size_up(3 * _space_alignment, _gen_alignment);
}
void GenCollectorPolicy::initialize_size_info() {
//调用父类方法
CollectorPolicy::initialize_size_info();
size_t max_new_size = 0;
if (!FLAG_IS_DEFAULT(MaxNewSize)) {
max_new_size = MaxNewSize;
} else {
//按照NewRatio计算允许的新的generation的内存最大值
max_new_size = scale_by_NewRatio_aligned(_max_heap_byte_size);
max_new_size = MIN2(MAX2(max_new_size, NewSize), MaxNewSize);
}
assert(max_new_size > 0, "All paths should set max_new_size");
if (_max_heap_byte_size == _min_heap_byte_size) {
_min_gen0_size = max_new_size;
_initial_gen0_size = max_new_size;
_max_gen0_size = max_new_size;
} else {
size_t desired_new_size = 0;
//通过命令行设置的
if (FLAG_IS_CMDLINE(NewSize)) {
// If NewSize is set on the command line, we must use it as
// the initial size and it also makes sense to use it as the
// lower limit.
_min_gen0_size = NewSize;
desired_new_size = NewSize;
max_new_size = MAX2(max_new_size, NewSize);
} else if (FLAG_IS_ERGO(NewSize)) { //通过代码计算重置的
// If NewSize is set ergonomically, we should use it as a lower
// limit, but use NewRatio to calculate the initial size.
_min_gen0_size = NewSize;
desired_new_size =
MAX2(scale_by_NewRatio_aligned(_initial_heap_byte_size), NewSize);
max_new_size = MAX2(max_new_size, NewSize);
} else {
//NewSize是默认值
_min_gen0_size = MAX2(scale_by_NewRatio_aligned(_min_heap_byte_size), NewSize);
desired_new_size =
MAX2(scale_by_NewRatio_aligned(_initial_heap_byte_size), NewSize);
}
assert(_min_gen0_size > 0, "Sanity check");
_initial_gen0_size = desired_new_size;
_max_gen0_size = max_new_size;
// Bound the sizes by the corresponding overall heap sizes.
_min_gen0_size = bound_minus_alignment(_min_gen0_size, _min_heap_byte_size);
_initial_gen0_size = bound_minus_alignment(_initial_gen0_size, _initial_heap_byte_size);
_max_gen0_size = bound_minus_alignment(_max_gen0_size, _max_heap_byte_size);
// Final check min <= initial <= max
_min_gen0_size = MIN2(_min_gen0_size, _max_gen0_size);
_initial_gen0_size = MAX2(MIN2(_initial_gen0_size, _max_gen0_size), _min_gen0_size);
_min_gen0_size = MIN2(_min_gen0_size, _initial_gen0_size);
}
//重置NewSize
if (NewSize != _initial_gen0_size) {
FLAG_SET_ERGO(uintx, NewSize, _initial_gen0_size);
}
重置MaxNewSize
if (MaxNewSize != _max_gen0_size) {
FLAG_SET_ERGO(uintx, MaxNewSize, _max_gen0_size);
}
if (PrintGCDetails && Verbose) {
gclog_or_tty->print_cr("1: Minimum gen0 " SIZE_FORMAT " Initial gen0 "
SIZE_FORMAT " Maximum gen0 " SIZE_FORMAT,
_min_gen0_size, _initial_gen0_size, _max_gen0_size);
}
DEBUG_ONLY(GenCollectorPolicy::assert_size_info();)
}
size_t GenCollectorPolicy::scale_by_NewRatio_aligned(size_t base_size) {
return align_size_down_bounded(base_size / (NewRatio + 1), _gen_alignment);
}
size_t GenCollectorPolicy::bound_minus_alignment(size_t desired_size,
size_t maximum_size) {
size_t max_minus = maximum_size - _gen_alignment;
return desired_size < max_minus ? desired_size : max_minus;
}
2、mem_allocate_work
mem_allocate_work方法用于从Java堆中分配指定大小的内存块,并在必要时触发GC,具体的GC通过VM_GenCollectForAllocation完成,其调用链如下:
GenCollectedHeap中的两个调用方法都是直接调用mem_allocate_work,以mem_allocate为例,如下:
该方法的实现如下:
HeapWord* GenCollectorPolicy::mem_allocate_work(size_t size,
bool is_tlab,
bool* gc_overhead_limit_was_exceeded) {
//获取Java堆对象
GenCollectedHeap *gch = GenCollectedHeap::heap();
debug_only(gch->check_for_valid_allocation_state());
//校验当前没有GC
assert(gch->no_gc_in_progress(), "Allocation during gc not allowed");
//默认情况下gc_overhead_limit_was_exceeded是false,只有当GC耗时超过限制了才会置为true
*gc_overhead_limit_was_exceeded = false;
HeapWord* result = NULL;
//不断循环尝试直到分配成功或者触发GC
for (uint try_count = 1, gclocker_stalled_count = 0; /* return or throw */; try_count += 1) {
//通过析构函数自动丢弃掉在每次循环过程中分配的Handle
HandleMark hm; // discard any handles allocated in each iteration
//获取年轻代
Generation *gen0 = gch->get_gen(0);
//校验其是否支持线性分配方式分配一段地址连续的内存
assert(gen0->supports_inline_contig_alloc(),
"Otherwise, must do alloc within heap lock");
//是否应该从该Generation分配,如果size过大则应该在老年代分配
if (gen0->should_allocate(size, is_tlab)) {
//Generation内部加锁,分配指定大小的内存
result = gen0->par_allocate(size, is_tlab);
if (result != NULL) {
//分配成功,is_in_reserved方法检查某个指针是否在堆内存范围内
assert(gch->is_in_reserved(result), "result not in heap");
return result;
}
}
//如果不能在年轻代分配或者年轻代分配失败
uint gc_count_before; // read inside the Heap_lock locked region
{
//获取Heap_lock锁
MutexLocker ml(Heap_lock);
if (PrintGC && Verbose) {
gclog_or_tty->print_cr("TwoGenerationCollectorPolicy::mem_allocate_work:"
" attempting locked slow path allocation");
}
//first_only表示是否只尝试一次,should_try_older_generation_allocation方法返回是否尝试在老年代分配
//只有大对象才会在老年代分配
bool first_only = ! should_try_older_generation_allocation(size);
//遍历所有的Generation尝试分配
result = gch->attempt_allocation(size, is_tlab, first_only);
if (result != NULL) {
//分配成功
assert(gch->is_in_reserved(result), "result not in heap");
return result;
}
//在其他的Generation中同样分配失败
//有线程处于JNI关键区且需要GC
if (GC_locker::is_active_and_needs_gc()) {
if (is_tlab) {
//如果TLAB,调用方会自动重试,其他线程会触发GC,所以这里返回NULL
return NULL; // Caller will retry allocating individual object
}
//如果某个Generation还能扩展
if (!gch->is_maximal_no_gc()) {
//尝试扩展Java堆并分配
result = expand_heap_and_allocate(size, is_tlab);
if (result != NULL) {
//分配成功
return result;
}
}
//所有Generation都不能扩展了,只能GC了
//GCLockerRetryAllocationCount表示当因为GC锁被阻塞了,尝试分配内存的次数,默认值是2
if (gclocker_stalled_count > GCLockerRetryAllocationCount) {
return NULL; // we didn't get to do a GC and we didn't get any memory
}
//如果这个线程不在JNI关键区,则会阻塞当前线程,等待所有处于JNI关键区的线程从关键区中退出了,最后一个退出的线程会触发GC,避免触发更多的没必要的GC。
JavaThread* jthr = JavaThread::current();
//判断当前线程是否处于JNI关键区内
if (!jthr->in_critical()) {
MutexUnlocker mul(Heap_lock);
//阻塞当前线程直到处于JNI关键区的线程从关键区中退出了
GC_locker::stall_until_clear();
gclocker_stalled_count += 1;
//GC结束,在下一次循环中尝试分配内存
continue;
} else {
//如果当前线程在JNI关键区内,CheckJNICalls表示是否校验JNI调用的参数,默认为false
if (CheckJNICalls) {
fatal("Possible deadlock due to allocating while"
" in jni critical section");
}
//返回NULL,因为处于关键区中的线程没法GC,没法继续分配内存
return NULL;
}
}
//获取GC的累计次数
gc_count_before = Universe::heap()->total_collections();
}
//处于关键区的线程退出了并执行GC了依然分配失败
//没有线程处于关键区中,分配失败,触发GC
VM_GenCollectForAllocation op(size, is_tlab, gc_count_before);
//当前线程会被阻塞直到GC执行完成
VMThread::execute(&op);
//如果执行了GC,返回false表示该次GC被跳过了,可能其他线程执行了GC
if (op.prologue_succeeded()) {
result = op.result();
//gc_locked返回true表示GC后依然分配失败,需要再次GC
if (op.gc_locked()) {
assert(result == NULL, "must be NULL if gc_locked() is true");
continue; // retry and/or stall as necessary
}
//GC完成且内存分配成功,如果GC耗时超过限制了则会返回NULL并抛出异常,然后重置gc_overhead_limit_exceeded
//这样gc_overhead_limit_exceeded不会持久化
//gc_overhead_limit_exceeded返回是否GC耗时超过限制
const bool limit_exceeded = size_policy()->gc_overhead_limit_exceeded();
//all_soft_refs_clear返回是否清楚了所有的软引用
const bool softrefs_clear = all_soft_refs_clear();
if (limit_exceeded && softrefs_clear) {
*gc_overhead_limit_was_exceeded = true;
//重置gc_overhead_limit_exceeded属性
size_policy()->set_gc_overhead_limit_exceeded(false);
if (op.result() != NULL) {
//填充对象,因为GC耗时超了,为了避免后期GC耗时越来越长,所以认为内存分配失败,需要抛出异常
CollectedHeap::fill_with_object(op.result(), size);
}
return NULL;
}
assert(result == NULL || gch->is_in_reserved(result),
"result not in heap");
return result;
}
//本次GC被跳过了,其他线程执行了GC,再次循环尝试分配内存
//QueuedAllocationWarningCount表示尝试了一定次数后就打印warning日志,默认值是0
if ((QueuedAllocationWarningCount > 0) &&
(try_count % QueuedAllocationWarningCount == 0)) {
warning("TwoGenerationCollectorPolicy::mem_allocate_work retries %d times \n\t"
" size=" SIZE_FORMAT " %s", try_count, size, is_tlab ? "(TLAB)" : "");
}
}
}
//是否应该尝试在老年代分配
bool GenCollectorPolicy::should_try_older_generation_allocation(
size_t word_size) const {
GenCollectedHeap* gch = GenCollectedHeap::heap();
//获取年轻代的当前可用空间
size_t gen0_capacity = gch->get_gen(0)->capacity_before_gc();
return (word_size > heap_word_size(gen0_capacity))
|| GC_locker::is_active_and_needs_gc()
|| gch->incremental_collection_failed();
}
HeapWord* GenCollectorPolicy::expand_heap_and_allocate(size_t size,
bool is_tlab) {
GenCollectedHeap *gch = GenCollectedHeap::heap();
HeapWord* result = NULL;
//遍历所有的Generation
for (int i = number_of_generations() - 1; i >= 0 && result == NULL; i--) {
Generation *gen = gch->get_gen(i);
if (gen->should_allocate(size, is_tlab)) {
//如果Generation可以分配,则尝试扩展并分配
result = gen->expand_and_allocate(size, is_tlab);
}
}
assert(result == NULL || gch->is_in_reserved(result), "result not in heap");
return result;
}
bool all_soft_refs_clear() { return _all_soft_refs_clear; }
上述代码中涉及的JNI关键区的概念可以参考《Hotspot JNIEnv API详解(二)》。
3、satisfy_failed_allocation
satisfy_failed_allocation就是上一个方法mem_allocate_work内存分配失败时执行GC的VM_GenCollectForAllocation的底层实现,该方法的调用链如下:
VM_GenCollectForAllocation::doit方法的调用如下:
该方法的实现如下:
HeapWord* GenCollectorPolicy::satisfy_failed_allocation(size_t size,
bool is_tlab) {
//获取Java堆
GenCollectedHeap *gch = GenCollectedHeap::heap();
//初始化GCCause
GCCauseSetter x(gch, GCCause::_allocation_failure);
HeapWord* result = NULL;
assert(size != 0, "Precondition violated");
if (GC_locker::is_active_and_needs_gc()) {
// GC_locker是active说明还有线程处于JNI关键区未开始GC
if (!gch->is_maximal_no_gc()) {
result = expand_heap_and_allocate(size, is_tlab);
}
return result; // could be null if we are out of space
} else if (!gch->incremental_collection_will_fail(false /* don't consult_young */)) {
//incremental_collection_will_fail表示如果执行增量收集是否会失败,参考上一次增量收集的结果
//如果可以执行增量收集
gch->do_collection(false /* full */,
false /* clear_all_soft_refs */,
size /* size */,
is_tlab /* is_tlab */,
number_of_generations() - 1 /* max_level */);
} else {
if (Verbose && PrintGCDetails) {
gclog_or_tty->print(" :: Trying full because partial may fail :: ");
}
//执行全量的full GC
gch->do_collection(true /* full */,
false /* clear_all_soft_refs */,
size /* size */,
is_tlab /* is_tlab */,
number_of_generations() - 1 /* max_level */);
}
//尝试分配内存
result = gch->attempt_allocation(size, is_tlab, false /*first_only*/);
if (result != NULL) {
//分配成功
assert(gch->is_in_reserved(result), "result not in heap");
return result;
}
//分配失败,尝试扩展Java堆并分配内存
result = expand_heap_and_allocate(size, is_tlab);
if (result != NULL) {
return result;
}
//分配失败,说明已经内存不足了,这时需要强制清理软引用,强制压实Java堆,任何可能的获取可用内存的方法都会执行,尽管代价昂贵
//如果尝试失败,抛出OOM异常
{
UIntFlagSetting flag_change(MarkSweepAlwaysCompactCount, 1); // Make sure the heap is fully compacted
gch->do_collection(true /* full */,
true /* clear_all_soft_refs */,
size /* size */,
is_tlab /* is_tlab */,
number_of_generations() - 1 /* max_level */);
}
//再次尝试分配内存
result = gch->attempt_allocation(size, is_tlab, false /* first_only */);
if (result != NULL) {
assert(gch->is_in_reserved(result), "result not in heap");
return result;
}
//分配失败,软引用已经清理的,标志位被置为false
assert(!should_clear_all_soft_refs(),
"Flag should have been handled and cleared prior to this point");
return NULL;
}
bool should_clear_all_soft_refs() { return _should_clear_all_soft_refs; }