目录

一、VM_Operation

​二、VMThread

1、定义

2、create / destroy

3、run / wait_for_vm_thread_exit

4、loop

5、VMThread::execute((VM_Operation*)

三、VM_GC_Operation

1、定义

2、doit_prologue / doit_epilogue

四、VM_CollectForMetadataAllocation

五、VM_GenCollectForAllocation


在上一篇《Hotspot 垃圾回收之CollectorPolicy (一) 源码解析》中讲到了用于执行GC的VM_CollectForMetadataAllocation和VM_GenCollectForAllocation,这两个类其实都是VM_Operation的子类,本篇博客就详细探讨VM_Operation和负责执行VM_Operation的VMThread的实现,从而透彻理解上一篇中相关方法的代码逻辑。

一、VM_Operation

VM_Operation定义在hotspot/src/share/vm/runtime/vm_operations.hpp中,表示一类在Java线程中完成初始化在JVM线程中执行的操作,比如因元空间不足触发垃圾回收并在回收结束后尝试分配指定大小的内存的VM_CollectForMetadataAllocation。VM_Operation定义了一个枚举Mode来描述执行的操作模式,如下:

blocking表示执行该动作需要加锁,safepoint表示必须在JVM处于安全点的时候才能执行。

还定义了一个枚举VMOp_Type来描述所有执行的操作,如下:

宏VM_OP_ENUM和VM_OPS_DO的定义如下:

#define VM_OP_ENUM(type)   VMOp_##type,// Note: When new VM_XXX comes up, add 'XXX' to the template table.
#define VM_OPS_DO(template)                       \template(Dummy)                                 \template(ThreadStop)                            \template(ThreadDump)                            \template(PrintThreads)                          \template(FindDeadlocks)                         \template(ForceSafepoint)                        \template(ForceAsyncSafepoint)                   \template(Deoptimize)                            \template(DeoptimizeFrame)                       \template(DeoptimizeAll)                         \template(ZombieAll)                             \template(UnlinkSymbols)                         \template(Verify)                                \template(PrintJNI)                              \template(HeapDumper)                            \template(DeoptimizeTheWorld)                    \template(CollectForMetadataAllocation)          \template(GC_HeapInspection)                     \template(GenCollectFull)                        \template(GenCollectFullConcurrent)              \template(GenCollectForAllocation)               \template(ParallelGCFailedAllocation)            \template(ParallelGCSystemGC)                    \template(CGC_Operation)                         \template(CMS_Initial_Mark)                      \template(CMS_Final_Remark)                      \template(G1CollectFull)                         \template(G1CollectForAllocation)                \template(G1IncCollectionPause)                  \template(DestroyAllocationContext)              \template(EnableBiasedLocking)                   \template(RevokeBias)                            \template(BulkRevokeBias)                        \template(PopulateDumpSharedSpace)               \template(JNIFunctionTableCopier)                \template(RedefineClasses)                       \template(GetOwnedMonitorInfo)                   \template(GetObjectMonitorUsage)                 \template(GetCurrentContendedMonitor)            \template(GetStackTrace)                         \template(GetMultipleStackTraces)                \template(GetAllStackTraces)                     \template(GetThreadListStackTraces)              \template(GetFrameCount)                         \template(GetFrameLocation)                      \template(ChangeBreakpoints)                     \template(GetOrSetLocal)                         \template(GetCurrentLocation)                    \template(EnterInterpOnlyMode)                   \template(ChangeSingleStep)                      \template(HeapWalkOperation)                     \template(HeapIterateOperation)                  \template(ReportJavaOutOfMemory)                 \template(JFRCheckpoint)                         \template(Exit)                                  \template(LinuxDllLoad)                          \template(RotateGCLog)                           \template(WhiteBoxOperation)                     \template(ClassLoaderStatsOperation)             \

与枚举VMOp_Type对应的就是VM_Operation的类继承关系,基本每个操作都有一个单独的子类去实现,部分如下:

本篇博客重点关注在上一篇博客中出现的VM_CollectForMetadataAllocation和VM_GenCollectForAllocation的实现,后续博客会逐一讲解用到的关键子类。

VM_Operation定义的属性比较简单,如下:

_calling_thread表示调用线程,_priority表示线程优先级,_timestamp表示该VM_Operation初始化的时间,_next和_prev用于将所有的VM_Operation串成一个链表,_names表示该VM_Operation子类的名称。

VM_Operation定义的方法主要是上述属性的读写,获取操作类型等,重点关注子类以下方法的实现:

  • doit:具体执行VM_Operation的方法,通过evaluate方法调用,子类不能改写evaluate方法的实现
  • doit_prologue:用于执行准备工作,当Java线程调用VMThread::execute((VM_Operation*)执行某个VM_Operation时会先执行doit_prologue,如果该方法返回true才会执行evaluate方法,否则被取消不执行。
  • doit_epilogue:用于执行某些依赖于VM_Operation执行结果的动作,当VM_Operation执行完成,Java线程会调用doit_epilogue方法一次。

上述三个方法的调用逻辑参考下面的VMThread::execute方法的分析。evaluate方法的默认实现如下图:


二、VMThread

1、定义

VMThread的定义在hotspot/src/share/vm/runtime/vMThread.hpp中,表示一个特殊的专门用来执行比较耗时的VM_Operation的原生线程,该类的类继承关系如下:

Thread本身定义的属性和方法非常庞杂,在用到了具体某个属性的时候再做探讨,这里重点关注VMThread本身的属性,如下:

  • static ThreadPriority _current_priority;  //线程的优先级
  • static bool _should_terminate; //是否应该终止
  • static bool _terminated; //是否终止
  • static Monitor * _terminate_lock;  //终止动作对应的锁
  • static PerfCounter* _perf_accumulated_vm_operation_time; //累计的执行VM_Operation的耗时
  • static VM_Operation*     _cur_vm_operation;   //当前执行的VM operation
  • tatic VMOperationQueue* _vm_queue;   // 缓存待执行的VM operation 队列
  • static VMThread*     _vm_thread; //唯一的VMThread实例

VMThread定义的方法并不复杂,除去属性相关的,就是执行VM operation相关的方法了,重点关注以下方法的实现。

2、create / destroy

这两方法都是静态方法,用来创建和销毁唯一的VMThread实例,重点关注其调用链,如下:

两方法源码实现如下:

void VMThread::create() {assert(vm_thread() == NULL, "we can only allocate one VMThread");//初始化各静态属性_vm_thread = new VMThread();// Create VM operation queue_vm_queue = new VMOperationQueue();guarantee(_vm_queue != NULL, "just checking");_terminate_lock = new Monitor(Mutex::safepoint, "VMThread::_terminate_lock", true);if (UsePerfData) {//如果开启UsePerfDataThread* THREAD = Thread::current();_perf_accumulated_vm_operation_time =PerfDataManager::create_counter(SUN_THREADS, "vmOperationTime",PerfData::U_Ticks, CHECK);}
}VMThread::VMThread() : NamedThread() {set_name("VM Thread");
}void VMThread::destroy() {if (_vm_thread != NULL) {//销毁_vm_threaddelete _vm_thread;_vm_thread = NULL;      // VM thread is gone}
}

3、run / wait_for_vm_thread_exit

run方法就是该线程启动后执行的具体逻辑,wait_for_vm_thread_exit用于等待VMThread退出,这两方法的调用链如下:

其中Threads::create_vm方法的调用如下:

os::create_thread方法是创建一个跟VMThread对应的表示原生线程的OSThread,os::start_thread就是启动该原生线程在原生线程中执行run方法,vmthread->active_handles()返回值不为NULL说明该线程已经开始执行run方法,返回NULL的话就阻塞当前线程等待VMThread开始执行run方法。这两方法的实现如下:

void VMThread::run() {assert(this == vm_thread(), "check");//初始化当前线程的ThreadLocalStoragethis->initialize_thread_local_storage();//设置对应的原生线程的线程名this->set_native_thread_name(this->name());//记录当前线程的栈帧的基地址和栈帧的最大深度等,并初始化原生线程this->record_stack_base_and_size();//设置active_handles,并唤醒在Notify_lock等待的线程,通知其VMThread已启动//在这之后Notify_lock会被Threads::create_vm()方法销毁this->set_active_handles(JNIHandleBlock::allocate_block());{MutexLocker ml(Notify_lock);Notify_lock->notify();}//VMThreadPriority表示VMThread运行的优先级,默认是-1,如果是默认值则采用NearMaxPriority,优先级是9,正常的是5int prio = (VMThreadPriority == -1)? os::java_to_os_priority[NearMaxPriority]: VMThreadPriority;//设置线程优先级os::set_native_priority( this, prio );//不断的循环执行loop方法,该方法会不断从_vm_queue队列中获取待执行的VM Operationthis->loop();//循环退出,准备线程销毁if (xtty != NULL) {//打印日志ttyLocker ttyl;xtty->begin_elem("destroy_vm");xtty->stamp();xtty->end_elem();assert(should_terminate(), "termination flag must be set");}//VMThread退出必须在安全点上SafepointSynchronize::begin();//VerifyBeforeExit表示在退出前校验系统,默认为falseif (VerifyBeforeExit) {HandleMark hm(VMThread::vm_thread());// Among other things, this ensures that Eden top is correct.Universe::heap()->prepare_for_verify();os::check_heap();// Silent verification so as not to pollute normal output,// unless we really asked for it.Universe::verify(!(PrintGCDetails || Verbose) || VerifySilently);}//通知CompileBroker停止编译CompileBroker::set_should_block();//等待所有本地线程如编译线程退出VM_Exit::wait_for_threads_in_native_to_block();// signal other threads that VM process is gone{//获取锁_terminate_lockMutexLockerEx ml(_terminate_lock, Mutex::_no_safepoint_check_flag);//将_terminated属性置为true表示线程已退出_terminated = true;//通知等待的线程_terminate_lock->notify();}// Thread destructor usually does this.ThreadLocalStorage::set_thread(NULL);}void VMThread::wait_for_vm_thread_exit() {//获取锁VMOperationQueue_lock,将_should_terminate置为true,表示VMThread准备退出了{ MutexLocker mu(VMOperationQueue_lock);_should_terminate = true;VMOperationQueue_lock->notify();}//获取锁_terminate_lock,等待_terminated属性变为true{ MutexLockerEx ml(_terminate_lock, Mutex::_no_safepoint_check_flag);while(!VMThread::is_terminated()) {_terminate_lock->wait(Mutex::_no_safepoint_check_flag);}}
}

4、loop

loop方法就是run方法中执行的核心业务逻辑了,该方法不断从待执行的VM Operation队列_vm_queue中获取待执行的VM_Operation实例,然后调用其evaluate方法,其实现如下:

void VMThread::loop() {//初始状态下_cur_vm_operation为NULLassert(_cur_vm_operation == NULL, "no current one should be executing");while(true) {VM_Operation* safepoint_ops = NULL;//获取锁VMOperationQueue_lock{ MutexLockerEx mu_queue(VMOperationQueue_lock,Mutex::_no_safepoint_check_flag);//开启一个新的循环_cur_vm_operation会置为NULLassert(_cur_vm_operation == NULL, "no current one should be executing");//获取下一个待执行的VM_Operation_cur_vm_operation = _vm_queue->remove_next();//PrintVMQWaitTime表示打印出VM_Operation在队列中的等待时间,默认为falseif (PrintVMQWaitTime && _cur_vm_operation != NULL &&!_cur_vm_operation->evaluate_concurrently()) {//计算等待耗时long stall = os::javaTimeMillis() - _cur_vm_operation->timestamp();if (stall > 0)tty->print_cr("%s stall: %Ld",  _cur_vm_operation->name(), stall);}//should_terminate方法false表示VMThread正常执行,返回true表示准备退出了while (!should_terminate() && _cur_vm_operation == NULL) {//没有待执行的VM_Operation,则等待一段时间bool timedout =VMOperationQueue_lock->wait(Mutex::_no_safepoint_check_flag,GuaranteedSafepointInterval);//SelfDestructTimer表示经过一段时间后自动退出,单位是分,默认是0if ((SelfDestructTimer != 0) && !is_error_reported() &&(os::elapsedTime() > SelfDestructTimer * 60)) {tty->print_cr("VM self-destructed");exit(-1);}//timedout为true,表示已经正常等待了指定时间//SafepointALot表示是否生成一堆安全点,与GuaranteedSafepointInterval配合使用,默认为false//is_cleanup_needed返回true表示需要执行清理动作了if (timedout && (SafepointALot ||SafepointSynchronize::is_cleanup_needed())) {MutexUnlockerEx mul(VMOperationQueue_lock,Mutex::_no_safepoint_check_flag);//强制进入一个安全点SafepointSynchronize::begin();SafepointSynchronize::end();}//尝试从队列获取待执行的VM_Operation_cur_vm_operation = _vm_queue->remove_next();// 如果获取的VM_Operation必须在安全点执行,则if (_cur_vm_operation != NULL &&_cur_vm_operation->evaluate_at_safepoint()) {//从需要在安全点执行的VM_Operation队列中获取一个待执行的safepoint_ops = _vm_queue->drain_at_safepoint_priority();}//如果获取VM_Operation失败,则需要循环等待}//已经获取待执行的VM_Operation,如果线程准备退出了则终止最外层的while循环,退出loop方法if (should_terminate()) break;} // Release mu_queue_lock//释放锁VMOperationQueue_lock,准备执行获取的VM_Operation{ HandleMark hm(VMThread::vm_thread());EventMark em("Executing VM operation: %s", vm_operation()->name());assert(_cur_vm_operation != NULL, "we should have found an operation to execute");//VMThreadHintNoPreempt是Solaris使用的,JDK11以上已废弃该选项if( VMThreadHintNoPreempt )os::hint_no_preempt();// If we are at a safepoint we will evaluate all the operations that// follow that also require a safepoint//如果该操作要求在安全点上执行if (_cur_vm_operation->evaluate_at_safepoint()) {//将其作为表示已经执行的VM Operation的_drain_list,方便oops_do遍历的时候可以遍历该Operation_vm_queue->set_drain_list(safepoint_ops); // ensure ops can be scanned//开启安全点SafepointSynchronize::begin();//执行该操作evaluate_operation(_cur_vm_operation);do {//执行所有需要在安全点执行的操作_cur_vm_operation = safepoint_ops;if (_cur_vm_operation != NULL) {do {// evaluate_operation deletes the op object so we have// to grab the next op nowVM_Operation* next = _cur_vm_operation->next();_vm_queue->set_drain_list(next);evaluate_operation(_cur_vm_operation);_cur_vm_operation = next;//PrintSafepointStatistics表示打印安全点同步的静态数据,默认为falseif (PrintSafepointStatistics) {SafepointSynchronize::inc_vmop_coalesced_count();}} while (_cur_vm_operation != NULL);}//假如又有新的线程加入了一个需要在安全点下执行的操作,则再一次尝试获取,如果获取成功则继续在安全点内处理//这么做的目的是尽可能减少安全点if (_vm_queue->peek_at_safepoint_priority()) {// must hold lock while draining queueMutexLockerEx mu_queue(VMOperationQueue_lock,Mutex::_no_safepoint_check_flag);safepoint_ops = _vm_queue->drain_at_safepoint_priority();} else {safepoint_ops = NULL;}} while(safepoint_ops != NULL);//oop遍历是在安全点下执行的_vm_queue->set_drain_list(NULL);//退出安全点SafepointSynchronize::end();} else {  // 不需要在安全点下执行的操作//TraceLongCompiles表示如果执行操作的时间超过指定时间LongCompileThreshold则打印日志if (TraceLongCompiles) {elapsedTimer t;t.start();evaluate_operation(_cur_vm_operation);t.stop();//计算耗时double secs = t.seconds();if (secs * 1e3 > LongCompileThreshold) {tty->print_cr("vm %s: %3.7f secs]", _cur_vm_operation->name(), secs);}} else {evaluate_operation(_cur_vm_operation);}//执行完成将_cur_vm_operation置为NULL_cur_vm_operation = NULL;}}//通知VMOperationRequest_lock锁上等待的线程,一个VM_Operation执行完成{ MutexLockerEx mu(VMOperationRequest_lock,Mutex::_no_safepoint_check_flag);VMOperationRequest_lock->notify_all();}//判断是否需要再次进入安全点if (SafepointALot || SafepointSynchronize::is_cleanup_needed()) {long interval          = SafepointSynchronize::last_non_safepoint_interval();bool max_time_exceeded = GuaranteedSafepointInterval != 0 && (interval > GuaranteedSafepointInterval);if (SafepointALot || max_time_exceeded) {HandleMark hm(VMThread::vm_thread());SafepointSynchronize::begin();SafepointSynchronize::end();}}}
}void VMThread::evaluate_operation(VM_Operation* op) {ResourceMark rm;{PerfTraceTime vm_op_timer(perf_accumulated_vm_operation_time());HS_DTRACE_PROBE3(hotspot, vmops__begin, op->name(), strlen(op->name()),op->evaluation_mode());EventExecuteVMOperation event;op->evaluate();if (event.should_commit()) {//发布VMOperation执行事件bool is_concurrent = op->evaluate_concurrently();event.set_operation(op->type());event.set_safepoint(op->evaluate_at_safepoint());event.set_blocking(!is_concurrent);event.set_caller(is_concurrent ? 0 : op->calling_thread()->osthread()->thread_id());event.commit();}HS_DTRACE_PROBE3(hotspot, vmops__end, op->name(), strlen(op->name()),op->evaluation_mode());}//判断这个VM_Operation实例是否需要在执行完成释放bool c_heap_allocated = op->is_cheap_allocated();//如果不是并行执行,则修改calling_thread的计数器if (!op->evaluate_concurrently()) {op->calling_thread()->increment_vm_operation_completed_count();}//在increment_vm_operation_completed_count方法执行完成后再访问该VM_Operation实例是不安全的,因为该实例是在calling_thread//的栈帧上分配的,有可能已经被释放了if (c_heap_allocated) {//显示的释放该VM_Operation实例delete _cur_vm_operation;}
}static bool should_terminate()                  { return _should_terminate; }virtual bool evaluate_at_safepoint() const {return evaluation_mode() == _safepoint  ||evaluation_mode() == _async_safepoint;
}

5、VMThread::execute((VM_Operation*)

VMThread::execute((VM_Operation*) 方法是非VMThread线程调用的,用于执行某个VM_Operation,execute方法会先执行doit_prologue,该方法执行成功后将该操作放入一个等待队列中,然后等待VMThread执行该操作完成,最后再执行doit_epilogue。该方法的源码如下:

void VMThread::execute(VM_Operation* op) {//获取当前线程Thread* t = Thread::current();//如果是非VMThread线程if (!t->is_VM_thread()) {SkipGCALot sgcalot(t);    // avoid re-entrant attempts to gc-a-lot//判断该操作是否可以并行执行bool concurrent = op->evaluate_concurrently();if (!concurrent) {//非并行执行的需要检验安全点状态t->check_for_valid_safepoint_state(true);}//执行doit_prologue,如果返回false则退出if (!op->doit_prologue()) {return;   // op was cancelled}//doit_prologue执行成功,设置调用线程及其优先级op->set_calling_thread(t, Thread::get_priority(t));// 如果is_cheap_allocated返回true,则在该Operation执行完成后会被VMThread释放掉,无法再访问,也就没必要再执行doit_epilogue方法了bool execute_epilog = !op->is_cheap_allocated();assert(!concurrent || op->is_cheap_allocated(), "concurrent => cheap_allocated");// Get ticket number for non-concurrent VM operationsint ticket = 0;if (!concurrent) {//非并行执行的,获取当前线程的启动执行的operation计数,然后增加该计数ticket = t->vm_operation_ticket();}{//获取锁VMOperationQueue_lock->lock_without_safepoint_check();//_vm_queue是VMThread的静态属性,表示待执行的operation队列bool ok = _vm_queue->add(op);//记录加入到队列的时间op->set_timestamp(os::javaTimeMillis());//解锁VMOperationQueue_lock->notify();VMOperationQueue_lock->unlock();// VM_Operation got skippedif (!ok) {//如果添加到队列失败assert(concurrent, "can only skip concurrent tasks");if (op->is_cheap_allocated()) delete op;return;}}//添加到队列成功if (!concurrent) {//等待opaeration执行完成,注意只有Java线程才会在锁定的时候触发安全点检查MutexLocker mu(VMOperationRequest_lock);//vm_operation_completed_count方法返回已经完成的operations的数量,如果大于ticket说明添加到队列中的operation已经被执行完了while(t->vm_operation_completed_count() < ticket) {//wait方法的入参为true表示不执行安全点检查VMOperationRequest_lock->wait(!t->is_Java_thread());}}//opaeration执行完成,执行doit_epilogueif (execute_epilog) {op->doit_epilogue();}} else {//如果VMThred线程,一般情况不会进入这部分代码,VMThred线程执行Operation有另外的方法assert(t->is_VM_thread(), "must be a VM thread");//如果当前执行的operationVM_Operation* prev_vm_operation = vm_operation();if (prev_vm_operation != NULL) {// Check the VM operation allows nested VM operation. This normally not the case, e.g., the compiler// does not allow nested scavenges or compiles.//如果prev_vm_operation不允许执行内嵌的operationsif (!prev_vm_operation->allow_nested_vm_operations()) {fatal(err_msg("Nested VM operation %s requested by operation %s",op->name(), vm_operation()->name()));}//将prev_vm_operation的调用线程作为op的调用线程op->set_calling_thread(prev_vm_operation->calling_thread(), prev_vm_operation->priority());}EventMark em("Executing %s VM operation: %s", prev_vm_operation ? "nested" : "", op->name());// Release all internal handles after operation is evaluatedHandleMark hm(t);//修改_cur_vm_operation_cur_vm_operation = op;//如果op必须在安全点上执行,且当前线程不在安全点上if (op->evaluate_at_safepoint() && !SafepointSynchronize::is_at_safepoint()) {//开启安全点SafepointSynchronize::begin();//执行evaluateop->evaluate();SafepointSynchronize::end();} else {op->evaluate();}//释放内存if (op->is_cheap_allocated()) delete op;//恢复_cur_vm_operation_cur_vm_operation = prev_vm_operation;}
}virtual bool evaluate_concurrently() const {return evaluation_mode() == _concurrent ||evaluation_mode() == _async_safepoint;
}void VM_Operation::set_calling_thread(Thread* thread, ThreadPriority priority) {_calling_thread = thread;assert(MinPriority <= priority && priority <= MaxPriority, "sanity check");_priority = priority;
}static VM_Operation* vm_operation()             { return _cur_vm_operation;   }

Thread中跟VM_Operation相关的属性有两个,如下:

这两个都是只增不减的,一个表示由当前线程触发的VM_Operation的总的数量,一个表示由当前线程触发的并且已经执行完成的VM_Operation的数量,相关的方法如下:

三、VM_GC_Operation

1、定义

VM_GC_Operation继承自VM_Operation,其定义在hotspot/src/share/vm/gc_implementation/shared/vmGCOperation.hpp中,是所有执行垃圾回收的VM_Operation的基类,该类的类继承关系如下:

其中VM_CollectForMetadataAllocation,VM_CollectForAllocation及其子类都是内存分配失败触发垃圾回收,然后尝试分配内存的操作;VM_GenCollectFull,VM_GenCollectFullConcurrent,VM_ParallelGCSystemGC,VM_G1CollectFull是不断GC算法下执行FULL GC操作;VM_GC_HeapInspection用于打印类的直方图,查看已加载的类的种类和数量;VM_HeapDumper就是执行Java堆内存Dump的。

VM_GC_Operation新增了如下属性:

  • BasicLock      _pending_list_basic_lock; //等待中的偏向锁链表,简称PLL
  • uint           _gc_count_before;         // 获取PLL前的GC次数
  • uint           _full_gc_count_before;    // 获取PLL前的Full GC次数
  • bool           _full;                    //是否是Full GC
  • bool           _prologue_succeeded;      // 是否doit_prologue执行成功
  • GCCause::Cause _gc_cause;                //导致GC的原因
  • bool           _gc_locked;               // 是否已经获取GC的锁

VM_GC_Operation新增的方法都是属性相关的,比较简单,重点关注其提供的doit_prologue和doit_epilogue方法的默认实现。

2、doit_prologue / doit_epilogue

doit_prologue在具体的垃圾回收动作执行前执行,用于判断当前GC是否需要执行,因为存在多个线程都因为内存分配事变而触发GC的情形,只需要执行一次GC即可,如果需要执行则获取锁Heap_lock以及java_lang_ref_Reference类的静态对象锁,doit_epilogue在垃圾回收动作执行成功后执行,用于释放doit_prologue中获取的锁。两者的实现如下:

bool VM_GC_Operation::doit_prologue() {//校验当前线程是JavaThreadassert(Thread::current()->is_Java_thread(), "just checking");//校验_gc_cause的合法assert(((_gc_cause != GCCause::_no_gc) &&(_gc_cause != GCCause::_no_cause_specified)), "Illegal GCCause");if (!is_init_completed()) {//如果JVM未初始化完成则退出抛出异常vm_exit_during_initialization(err_msg("GC triggered before VM initialization completed. Try increasing ""NewSize, current value " UINTX_FORMAT "%s.",byte_size_in_proper_unit(NewSize),proper_unit_for_byte_size(NewSize)));}//获取java_lang_ref_Reference类的静态对象锁,该锁用于处理软引用acquire_pending_list_lock();//获取Heap_lockHeap_lock->lock();if (skip_operation()) {//如果跳过本次GC,则释放锁Heap_lock,释放java_lang_ref_Reference类的静态对象锁Heap_lock->unlock();release_and_notify_pending_list_lock();_prologue_succeeded = false;} else {//执行GC_prologue_succeeded = true;SharedHeap* sh = SharedHeap::heap();//通知SharedHeap已经获取了Heap_lock锁if (sh != NULL) sh->_thread_holds_heap_lock_for_gc = true;}return _prologue_succeeded;
}void VM_GC_Operation::doit_epilogue() {assert(Thread::current()->is_Java_thread(), "just checking");//释放锁Heap_lock和java_lang_ref_Reference类的静态对象锁SharedHeap* sh = SharedHeap::heap();if (sh != NULL) sh->_thread_holds_heap_lock_for_gc = false;Heap_lock->unlock();release_and_notify_pending_list_lock();
}void VM_GC_Operation::acquire_pending_list_lock() {//实际是获取java_lang_ref_Reference类的一个静态对象锁lockInstanceRefKlass::acquire_pending_list_lock(&_pending_list_basic_lock);
}//因为可能存在多个线程都因为内存分配失败从而触发GC的情形,但是我们只需要一次GC即可,需要跳过其他的GC请求
bool VM_GC_Operation::skip_operation() const {//如果不等于说明某个线程已经抢先执行了GCbool skip = (_gc_count_before != Universe::heap()->total_collections());if (_full && skip) {skip = (_full_gc_count_before != Universe::heap()->total_full_collections());}if (!skip && GC_locker::is_active_and_needs_gc()) {//is_maximal_no_gc方法返回堆内存已经提交的内存是否达到上限了,如果是true表示已到上限,必须GC//此时返回false则执行目标GC,返回true则处于JNI关键区的线程会执行GCskip = Universe::heap()->is_maximal_no_gc();assert(!(skip && (_gc_cause == GCCause::_gc_locker)),"GC_locker cannot be active when initiating GC");}return skip;
}void VM_GC_Operation::release_and_notify_pending_list_lock() {InstanceRefKlass::release_and_notify_pending_list_lock(&_pending_list_basic_lock);
}

四、VM_CollectForMetadataAllocation

VM_CollectForMetadataAllocation的定义同样在vmGCOperation.hpp中,为了能够在垃圾回收成功后给元数据分配内存,增加了几个元数据内存分配相关的属性,如下:

重点关注其核心的doit方法,其实现如下:

void VM_CollectForMetadataAllocation::doit() {SvcGCMarker sgcm(SvcGCMarker::FULL);CollectedHeap* heap = Universe::heap();//设置heap的_gc_causeGCCauseSetter gccs(heap, _gc_cause);//MetadataAllocationFailALot表示当元空间内存分配失败后是否间隔一段时间再重试,默认为falseif (!MetadataAllocationFailALot) {//再次尝试分配,可能其他线程分配失败已经触发了GC_result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);if (_result != NULL) {return;}}if (initiate_concurrent_GC()) {//再次尝试分配,因为GC线程是同当前线程并行执行的_result = _loader_data->metaspace_non_null()->expand_and_allocate(_size, _mdtype);if (_result != NULL) {return;}//记录内存分配失败log_metaspace_alloc_failure_for_concurrent_GC();}//执行GC,这时还未清理软引用heap->collect_as_vm_thread(GCCause::_metadata_GC_threshold);_result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);if (_result != NULL) {return;}//分配失败,尝试扩展Metaspace_result = _loader_data->metaspace_non_null()->expand_and_allocate(_size, _mdtype);if (_result != NULL) {return;}// 再次GC,这次会清理软引用heap->collect_as_vm_thread(GCCause::_last_ditch_collection);_result = _loader_data->metaspace_non_null()->allocate(_size, _mdtype);if (_result != NULL) {return;}//两次GC后依然分配失败,打印日志if (Verbose && PrintGCDetails) {gclog_or_tty->print_cr("\nAfter Metaspace GC failed to allocate size "SIZE_FORMAT, _size);}if (GC_locker::is_active_and_needs_gc()) {//将_gc_locked置为trueset_gc_locked();}
}//如果允许并发的卸载Klass则返回true
bool VM_CollectForMetadataAllocation::initiate_concurrent_GC() {
#if INCLUDE_ALL_GCS//CMSClassUnloadingEnabled表示在使用CMS算法时是否允许类卸载,默认为trueif (UseConcMarkSweepGC && CMSClassUnloadingEnabled) {//通知MetaspaceGC准备执行GC了MetaspaceGC::set_should_concurrent_collect(true);return true;}if (UseG1GC && ClassUnloadingWithConcurrentMark) {G1CollectedHeap* g1h = G1CollectedHeap::heap();g1h->g1_policy()->set_initiate_conc_mark_if_possible();GCCauseSetter x(g1h, _gc_cause);// At this point we are supposed to start a concurrent cycle. We// will do so if one is not already in progress.bool should_start = g1h->g1_policy()->force_initial_mark_if_outside_cycle(_gc_cause);if (should_start) {double pause_target = g1h->g1_policy()->max_pause_time_ms();g1h->do_collection_pause_at_safepoint(pause_target);}return true;}
#endifreturn false;
}static void log_metaspace_alloc_failure_for_concurrent_GC() {if (Verbose && PrintGCDetails) {if (UseConcMarkSweepGC) {gclog_or_tty->print_cr("\nCMS full GC for Metaspace");} else if (UseG1GC) {gclog_or_tty->print_cr("\nG1 full GC for Metaspace");}}
}

五、VM_GenCollectForAllocation

VM_GenCollectForAllocation继承自VM_CollectForAllocation,VM_CollectForAllocation增加了两个属性保存待分配的内存大小,如下:

VM_GenCollectForAllocation增加了一个属性,表示是否为TLAB分配内存,如下:

重点关注其doit方法实现,如下:

核心在GenCollectedHeap::satisfy_failed_allocation中,该方法的实现如下:

参考上一篇博客中CollectorPolicy:: satisfy_failed_allocation方法的实现。

Hotspot 垃圾回收之VM_Operation 源码解析相关推荐

  1. Hotspot 垃圾回收之GenCollectedHeap 源码解析

    目录 1.定义 2.构造方法 / initialize / post_initialize 3.do_collection 4.do_full_collection 5.collect 6.VM_Ge ...

  2. Hotspot 对象引用Reference和Finalizer 源码解析

    目录 一.Reference 1.SoftReference / WeakReference / PhantomReference 2.定义 3.ReferenceHandler 4.Cleaner ...

  3. android进阶篇02、RecyclerView回收复用机制源码解析,h5移动端开发进行定位

    public void removeAndRecycleViewAt(int index, @NonNull Recycler recycler) { final View view = getChi ...

  4. Hotspot 垃圾回收之oop_iterate(二) 源码解析

    目录 1.java.lang.Class 1.1.Class实例中oop_size.klass等属性是哪来的? 1.2._offset_of_static_fields 1.3 为什么从_offset ...

  5. Hotspot 垃圾回收之ReferenceProcessor(二) 源码解析

    目录 1.process_discovered_reflist 2.process_phaseJNI 3.process_discovered_references 4.preclean_discov ...

  6. Hotspot 重量级锁ObjectMonitor(一) 源码解析

    目录 1.定义 2.TrySpin_VaryDuration 3.ObjectWaiter 4.EnterI 5.JavaThreadBlockedOnMonitorEnterState / OSTh ...

  7. Python 的垃圾回收回收机制(源码)

    python内存管理及垃圾回收 1. 引用计数器 1.1 环状双向连表 refchain 在python程序中创建的任何对象都会放在refchain链表中,并且可以通过这个对象访问到上一个和下一个对象 ...

  8. Hotspot 重量级锁ObjectMonitor(二) 源码解析

    目录 1.AddWaiter / DequeueWaiter /DequeueSpecificWaiter 2.wait 3.notify 4.notifyAll 5.exit 6.try_enter ...

  9. Glide 4.9源码解析-缓存策略

    本文Glide源码基于4.9,版本下载地址如下:Glide 4.9 前言 在分析了Glide的图片加载流程后,更加发觉到Glide的强大,于是这篇文章将继续深入分析Glide的缓存策略.不过今天的文章 ...

最新文章

  1. 【Python】数据提取xpath和lxml模块(豆瓣电影排行榜的爬虫)
  2. React入门0x014: Fragment
  3. 皇室战争:暗夜女巫重做,去掉亡语机制后,她凉的可能会更快
  4. 虚幻4 Object和序列化
  5. L3-008. 喊山-PAT团体程序设计天梯赛GPLT(广度优先搜索)
  6. Atitit mybatis spring整合。读取spring、yml、文件的mysql url 步骤,读取yml,文件,使用ongl定位到url pwd usr 读取mybatis模板配置,
  7. JavaScript学习笔记(3)——JavaScript与HTML的组合方式
  8. android模拟器GPS简单应用(一)
  9. Navicat Premium 12破解方法
  10. 渝粤题库 陕西师范大学 《服务礼仪》作业
  11. 企业私有云建设需求分析
  12. 为什么手机浏览器打不开html文件,win7浏览器打不开本地html文件的原因及解决方法...
  13. 睡眠不好怎么办?提升睡眠质量的小妙招
  14. 无论多大年纪,请保留一份童真和幻想
  15. def read()
  16. 论如何科学的看小本子
  17. 猿创征文|2022年前端之路——我的前端开发好帮手
  18. selenium与js联动实现页面定位及删除页面元素的操作问题
  19. CodeForces 711C Coloring Trees (三维DP)
  20. ERROR: cannot launch node of type [robot_state_publisher/state_publisher]: Cannot locate node of typ

热门文章

  1. java bigdecimal取余_BigDecimal取余运算
  2. 端子排 和 冷压接头
  3. 正点原子阿尔法imx6ull的wm8960声卡驱动表层简析
  4. 网口压线顺序_手把手教学网线做法 掌握这几个步骤 轻松搞定
  5. 读瞬间之美 Web界面设计如何让用户心动 - 笔记
  6. link-group
  7. 主成分分析法(Principal Component Analysis,PCA)
  8. Zemax光学设计(十五) —— 三片摄影物镜(1)
  9. 如何分享 iPhone 网络到 Mac?
  10. Linux虚拟主机配置终极篇