前文 介绍了 G1 非常重要多的重要概念,并介绍了相关的源码,这些概念在后续文章中会重复提到。本文将开始介绍堆初始化相关的内容和 G1 中重要的组件。

启动 JVM

JVM进程本质上也是一个操作系统进程,通常我们使用下面的命令启动 Java 程序。

java -jar -Xmx1024m -Xms1024m yyy.jar

这里 java 是编译好的可执行文件,可在 shell 中执行。简单来说,操作系统会对文件的格式进行解析,创建出进程所需要的地址空间,然后跳转到 main 函数执行用户代码。

main 函数

JVM 是 c++ 开发的,入口函数应该是 main 函数:

//jdk/src/jdk.jpackage/linux/native/applauncher/LinuxLauncher.c
int main(int argc, char *argv[]) {
    //omit
    exitCode = launchJvm(jvmLauncherData);
}

下面简单地罗列调用栈,主要是函数指针的获取代码。

//-> launchJvm
JLI_Launch = dlsym(jliLibHandle, "JLI_Launch");

//->jvmLauncherStartJvm->JNIEXPORT int JNICALL JLI_Launch

//-> LoadJavaVM
ifn->CreateJavaVM = (CreateJavaVM_t) dlsym(libjvm, "JNI_CreateJavaVM");

//->JVMInit -> ContinueInNewThread -> CallJavaMainInNewThread

在函数 CallJavaMainInNewThread 中尝试创建新的线程执行虚拟机启动,然后执行 JavaMain

if (pthread_create(&tid, &attr, ThreadJavaMain, args) == 0) {
        pthread_join(tid, &tmp);
    } else {
        rslt = JavaMain(args);
    }

在函数 JavaMain 中可以看到很多熟悉的内容,比如 Java main 方法的调用,而且可以看到 main 没有参数也是可以运行的。

if (!InitializeJVM(&vm, &env, &ifn)) {}

mainClass = LoadMainClass(env, mode, what);
mainArgs = CreateApplicationArgs(env, argv, argc)

PostJVMInit(env, appClass, vm);

ret = invokeStaticMainWithoutArgs(env, mainClass);

ret = invokeStaticMainWithArgs(env, mainClass, mainArgs);

LoadMainClass函数内部调用 sun.launcher.LauncherHelper#checkAndLoadMain 获取主类对应的Class实例。

JVM 创建

接着看创建 JVM 代码,能看到很多熟悉的东西,agent、main thread、synchronizer、Metaspace。

//InitializeJVM->JNI_CreateJavaVM ->JNI_CreateJavaVM_inner
//-> JNI_CreateJavaVM_inner->Threads::create_vm
jint Threads::create_vm(JavaVMInitArgs* args, bool* canTryAgain) {
  
  JvmtiAgentList::load_agents()

  // Attach the main thread to this os thread
  JavaThread* main_thread = new JavaThread();
  
  // Initialize Java-Level synchronization subsystem
  ObjectMonitor::Initialize();
  ObjectSynchronizer::initialize();

    // Initialize global modules
  jint status = init_globals();

  Metaspace::post_initialize();
  //omit
}

Java 堆创建

接着看找到 JVM 堆创建的代码

//init_globals -> universe_init
GCConfig::arguments()->initialize_heap_sizes();

//->Universe::initialize_heap()
jint Universe::initialize_heap() {
  _collectedHeap = GCConfig::arguments()->create_heap();
  return _collectedHeap->initialize();
}
// _collectedHeap 是 G1CollectedHeap
G1CollectedHeap::initialize();

GC 类型选择

未指定 GC 的情况下,JVM 通过 select_gc_ergonomically 函数在服务端模式默认选择 G1。

// GCConfig::initialize()->select_gc()
GCArguments* GCConfig::select_gc() {
  if (is_no_gc_selected()) { select_gc_ergonomically(); }
  // Exactly one GC selected
  FOR_EACH_INCLUDED_GC(gc) {
    //返回 G1Arguments 
    if (gc->_flag) { return &gc->_arguments; }
  }
}

#ifndef INCLUDE_G1GC
#define INCLUDE_G1GC 1

void GCConfig::select_gc_ergonomically() {
  if (os::is_server_class_machine()) {
    #if INCLUDE_G1GC
    FLAG_SET_ERGO_IF_DEFAULT(UseG1GC, true);
    //omit
  } else { /*omit*/ }
}

G1CollectedHeap()

G1 堆属性很多,仅枚举比较重要的,具体逻辑使用时再次介绍。

// Handle G1 NUMA support. 与 G1 numa 优化有关,
//详情见 JEP 345: NUMA-Aware Memory Allocation for G1
G1NUMA* _numa;

// The sequence of all heap regions in the heap.
//负责对堆中所有 region 进行管理
G1HeapRegionManager _hrm;

// Manages all allocations with regions except humongous object allocations.
// 分配器
G1Allocator* _allocator;

// The young region list.
G1EdenRegions _eden;
G1SurvivorRegions _survivor;

//cset
G1CollectionSet _collection_set;

//年轻代共享的记忆集
G1CardSet _young_regions_cardset;
//收集策略,回收过程保存一下统计信息,用于预测回收时间
G1Policy* _policy;

//GC 线程数
ParallelGCThreads;
//全局任务队列
_task_queues

G1 堆初始化

initialize() 初始化内部组件,此处只是枚举重要属性,特别重要的会在单独的小节介绍。

//全局卡表,前面的文章介绍过
G1CardTable* ct = new G1CardTable(heap_rs.region());

//写屏障相关操作
G1BarrierSet* bs = new G1BarrierSet(ct);

//遍历记忆集相关
_rem_set = new G1RemSet(this, _card_table);

//GC 工作线程
  _workers = new WorkerThreads("GC Thread", ParallelGCThreads);

//并发标记相关
_cm = new G1ConcurrentMark(this, bitmap_storage);
_cm_thread = _cm->cm_thread();

//GC 策略相关初始化
policy()->init(this, &_collection_set);

//处理 dirty card 线程
jint ecode = initialize_concurrent_refinement();

//负责协调异步任务的执行和后台的维护操作
ecode = initialize_service_thread();
_service_thread->register_task(_periodic_gc_task);
_service_thread->register_task(_free_arena_memory_task);

G1 组件

WorkerThreads

处理特定任务的线程池,用于 GC 收集和 并发标记。_workers 是实际的线程数据。

_workers = new WorkerThreads("GC Thread", ParallelGCThreads);
_concurrent_workers = new WorkerThreads("G1 Conc", _max_concurrent_workers)

uint WorkerThreads::set_active_workers(uint num_workers) {
  while (_created_workers < num_workers) {
    WorkerThread* const worker = create_worker(_created_workers);
    _workers[_created_workers] = worker;
  }
}

WorkerThread 被创建完成以后就会执行 run 方法,阻塞在 _start_semaphore 等待被唤醒。

当任务执行完成则换醒分发任务的线程。

void WorkerThread::run() {
  while (true) { _dispatcher->worker_run_task(); }
}

void WorkerTaskDispatcher::worker_run_task() {
  _start_semaphore.wait();

  // Get and set worker id.
  const uint worker_id = Atomic::fetch_then_add(&_started, 1u);
  WorkerThread::set_worker_id(worker_id);

  // Run task.
  GCIdMark gc_id_mark(_task->gc_id()); //保存到工作线程,这里就是在日志中看到第几轮 GC 编号。

  _task->work(worker_id);
  const uint not_finished = Atomic::sub(&_not_finished, 1u);
  if (not_finished == 0) { _end_semaphore.signal(); /*任务执行完成,唤醒分发任务的线程。*/ }
}

coordinator_distribute_task 负责任务的分发,唤醒任务线程,并且等待任务执行完成。

void WorkerTaskDispatcher::coordinator_distribute_task(WorkerTask* task, uint num_workers) {
  _task = task;
  _not_finished = num_workers;

  _start_semaphore.signal(num_workers);//唤醒任务线程
  _end_semaphore.wait(); //等待任务执行完成

  _task = nullptr;
  _started = 0;
}

WorkerTask

WorkerTask 是顶级接口,几乎所有的 GC 任务都继承它,并且重写 work 方法。

后续阅读源码时,可直接查看 work 方法的逻辑。

class WorkerTask : public CHeapObj<mtInternal> {
  const char* _name;
  const uint _gc_id;
  virtual void work(uint worker_id) = 0;
}

G1ConcurrentMark

此类主要用于发标记阶段。

G1ConcurrentMarkThread* _cm_thread;     // The thread doing the work
// Concurrent marking support structures
G1CMBitMap   _mark_bitmap

G1CMTask**              _tasks;       // Task queue array (max_worker_id length)
G1CMTaskQueueSet*       _task_queues; // Task queue se

WorkerThreads* _concurrent_workers;
uint      _num_concurrent_workers; // The number of marking worker threads we're using
uint      _max_concurrent_workers; // Maximum number of marking worker thread>

_mark_bitmap:存储标记结果的 bitMap,1 bit 标记 8 bytes,它额外消耗堆内存的 1.5%(1/64)。 _concurrent_workers:工作线程前文已经说过。

G1ConcurrentMarkThread

负责并发标记的整体调度、任务分发

//继承树 oncurrentGCThread<-NamedThread<-NonJavaThread<-Thread<-ThreadShadow
class G1ConcurrentMarkThread: public ConcurrentGCThread {
    void run_service();
}
class ConcurrentGCThread: public NamedThread {
  void ConcurrentGCThread::run() { run_service(); }
}

void G1ConcurrentMarkThread::run_service() {

  while (wait_for_next_cycle()) { //等待被唤醒
    concurrent_cycle_start();
    concurrent_mark_cycle_do();
    concurrent_cycle_end(_state == FullMark && !_cm->has_aborted());
  }
}
//唤醒代码
//-> do_collection_pause_at_safepoint_helper()
//->start_concurrent_cycle()->CGC_lock->notify()

G1ConcurrentRefine

此类负责处理异步处理 dirty carddo_refinement_step 是入口函数。_dcqsdirty card 集合。

class G1ConcurrentRefine : public CHeapObj<mtGC> {
  G1DirtyCardQueueSet& _dcqs
  G1ConcurrentRefineThreadControl _thread_control;
}

//->G1ConcurrentRefineThreadControl::initialize
//->create_refinement_thread
//-> G1ConcurrentRefineThread::create(_cr, worker_id);
//crt = ->new (std::nothrow) G1PrimaryConcurrentRefineThread(cr);
//-> crt->create_and_start()

class G1ConcurrentRefineThread: public ConcurrentGCThread {
  void G1ConcurrentRefineThread::run_service() {

  while (wait_for_completed_buffers()) {
    SuspendibleThreadSetJoiner sts_join;
    G1ConcurrentRefineStats active_stats_start = _refinement_stats;
    while (!should_terminate()) {
      if (sts_join.should_yield()) {
      } else if (maybe_deactivate()) { break; }
       else {
        do_refinement_step(); //处理 dirty card
  } } } } }

G1ServiceThread

此类负责协调异步任务的执行和后台的维护操作

class G1ServiceThread: public ConcurrentGCThread {
    G1ServiceTaskQueue _task_queue;
    
    void G1ServiceThread::run_service() {
        while (true) {
        G1ServiceTask* task = wait_for_task();
        run_task(task);
    } } }
//注册任务
_service_thread->register_task(_periodic_gc_task);
_service_thread->register_task(_free_arena_memory_task);

总结

本文从 JVM 启动到 G1 堆,介绍了 G1 重要组件,G1 重要非常多,余下的后续使用时再介绍。