VM_Operation 源码剖析

源代码 2024-9-3 16:43:00 96 0 来自 中国
上文中讲到了用于实验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来形貌全部实验的操纵,如下:

2.png 宏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界说的属性比力简朴,如下:

4.png _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方法的默认实现如下图:

5.png 二、VMThread
1、界说
VMThread的界说在hotspot/src/share/vm/runtime/vMThread.hpp中,体现一个特殊的专门用来实验比力耗时的VM_Operation的原生线程,该类的类继承关系如下:
6.png 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
  • static 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) {    //如果开启UsePerfData    Thread* 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_thread    delete _vm_thread;    _vm_thread = NULL;      // VM thread is gone  }}3、run / wait_for_vm_thread_exit
run方法就是该线程启动后实验的详细逻辑,wait_for_vm_thread_exit用于等候VMThread退出,这两方法的调用链如下:
9.png 10.png 此中Threads::create_vm方法的调用如下:
11.png 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");  //初始化当前线程的ThreadLocalStorage  this->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,正常的是5  int prio = (VMThreadPriority == -1)    ? os::java_to_os_priority[NearMaxPriority]    : VMThreadPriority;  //设置线程优先级  os::set_native_priority( this, prio );   //不绝的循环实验loop方法,该方法会不绝从_vm_queue队列中获取待实验的VM Operation  this->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表如今退出前校验体系,默以为false  if (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_lock    MutexLockerEx 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为NULL  assert(_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会置为NULL      assert(_cur_vm_operation == NULL, "no current one should be executing");      //获取下一个待实验的VM_Operation      _cur_vm_operation = _vm_queue->remove_next();       //PrintVMQWaitTime体现打印出VM_Operation在队列中的等候时间,默以为false      if (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体现颠末一段时间后主动退出,单位是分,默认是0        if ((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 now              VM_Operation* next = _cur_vm_operation->next();              _vm_queue->set_drain_list(next);              evaluate_operation(_cur_vm_operation);              _cur_vm_operation = next;              //PrintSafepointStatistics体现打印安全点同步的静态数据,默以为false              if (PrintSafepointStatistics) {                SafepointSynchronize::inc_vmop_coalesced_count();              }            } while (_cur_vm_operation != NULL);          }          //如果又有新的线程参加了一个必要在安全点下实验的操纵,则再一次实验获取,如果获取乐成则继承在安全点内处理          //这么做的目的是尽大概淘汰安全点          if (_vm_queue->peek_at_safepoint_priority()) {            // must hold lock while draining queue            MutexLockerEx 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 operations    int 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 skipped      if (!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_epilogue    if (execute_epilog) {      op->doit_epilogue();    }  } else {    //如果VMThred线程,一样平常环境不会进入这部分代码,VMThred线程实验Operation有别的的方法    assert(t->is_VM_thread(), "must be a VM thread");    //如果当前实验的operation    VM_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不答应实验内嵌的operations      if (!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 evaluated    HandleMark hm(t);    //修改_cur_vm_operation    _cur_vm_operation = op;    //如果op必须在安全点上实验,且当前线程不在安全点上    if (op->evaluate_at_safepoint() && !SafepointSynchronize::is_at_safepoint()) {      //开启安全点      SafepointSynchronize::begin();      //实验evaluate      op->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的数目,干系的方法如下:

13.png 三、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() {  //校验当前线程是JavaThread  assert(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_lock  Heap_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类的一个静态对象锁lock  InstanceRefKlass::acquire_pending_list_lock(&_pending_list_basic_lock);}  //由于大概存在多个线程都由于内存分配失败从而触发GC的情况,但是我们只必要一次GC即可,必要跳过其他的GC哀求bool VM_GC_Operation::skip_operation() const {  //如果不即是分析某个线程已经抢先实验了GC  bool 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关键区的线程会实验GC    skip = 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_cause  GCCauseSetter gccs(heap, _gc_cause);   //MetadataAllocationFailALot体现当元空间内存分配失败后是否隔断一段时间再重试,默以为false  if (!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置为true    set_gc_locked();  }} //如果答应并发的卸载Klass则返回truebool VM_CollectForMetadataAllocation::initiate_concurrent_GC() {#if INCLUDE_ALL_GCS  //CMSClassUnloadingEnabled表如今利用CMS算法时是否答应类卸载,默以为true  if (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;  }#endif   return 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方法的实现。
您需要登录后才可以回帖 登录 | 立即注册

Powered by CangBaoKu v1.0 小黑屋藏宝库It社区( 冀ICP备14008649号 )

GMT+8, 2024-10-19 06:15, Processed in 0.224313 second(s), 35 queries.© 2003-2025 cbk Team.

快速回复 返回顶部 返回列表