G1 堆初始化
前文 介绍了 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 card
,do_refinement_step
是入口函数。_dcqs
是 dirty 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 重要非常多,余下的后续使用时再介绍。