VM_Operation 源码解析-后端论坛-技术分享-千百度社区

VM_Operation 源码解析

VM_Operation 源码解析

上文中讲到了用于执行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来描述执行的操作模式,如下:


image.png

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

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


image.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的类继承关系,基本每个操作都有一个单独的子类去实现,部分如下:


image.png

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

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


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


image.png

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

image.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实例,重点关注其调用链,如下:


image.png
image.png

两方法源码实现如下:

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退出,这两方法的调用链如下:

image.png
image.png

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

image.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相关的属性有两个,如下:


image.png

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


image.png

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

image.png

其中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中,为了能够在垃圾回收成功后给元数据分配内存,增加了几个元数据内存分配相关的属性,如下:

image.png

重点关注其核心的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则返回true
bool 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增加了两个属性保存待分配的内存大小,如下:


image.png

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


image.png

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

image.png

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

image.png

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

© 著作权归作者所有,转载或内容合作请联系作者

请登录后发表评论

    没有回复内容