Hotpot 年輕代GC 原始碼分析
Hotspot為每一個代 都設定了 各自的 level,也就是等級,年輕代 level 一般為 0, 老年代 level 一般為 1。
在預設的年輕代(/vm/memory/defNewGeneration.cpp)下的實現:
defNewGeneration::collect 有如下重要呼叫點:
1. gch->gen_process_strong_roots(_level, true, // Process younger gens, if any,// as strong roots. true, // activate StrongRootsScope false, // not collecting perm generation. SharedHeap::SO_AllClasses, &fsc_with_no_gc_barrier,true, // walk *all* scavengable nmethods &fsc_with_gc_barrier);
// "evacuate followers". 2. evacuate_followers.do_void();
3. rp->process_discovered_references(&is_alive, &keep_alive, &evacuate_followers, NULL);
重要的閉包:FastScanClosure :: do_oop_work :將 接收的引數是 T * (其實也就是 oop *) , 是指向 oop 的指標
oop 本身就是 oopDesc * 的縮寫,也就是 java 在 C++ 層面的物件的指標,所以修改 T* 指向的記憶體單元,就可以修改這個記憶體單元指向的oop,相當於修改物件的引用型別變數
入參的 do_barriar 如果為 true,則在處理 T* 時,需要將該引用指向的地址的卡表,設定為 younggen_card (未知作用)
概述:
1.gen_process_strong_roots
處理根節點引用的oop,包括Java方法堆疊,JNI方法堆疊,符號表等引用的物件
2.evacuate_followers
負責將 從年輕代promote到老年代的物件,使用 入參為 true 的 FastScanClosure 作為引數,呼叫這些物件的 oop_iterate, 也就是遍歷這些物件的引用型別
也就是遍歷這些物件的引用型別變數,如果年齡到閾值,則promote到老年代,否則複製到 to 區
3.處理Java提供的各種非強引用
1.gen_process_strong_roots
該方法的實現在GenCollectedHeap 中
// General strong roots. if (!do_code_roots) { SharedHeap::process_strong_roots(activate_scope, collecting_perm_gen, so, not_older_gens, NULL, older_gens); } else { bool do_code_marking = (activate_scope || nmethod::oops_do_marking_is_active()); CodeBlobToOopClosure code_roots(not_older_gens, /*do_marking=*/ do_code_marking); SharedHeap::process_strong_roots(activate_scope, collecting_perm_gen, so, not_older_gens, &code_roots, older_gens); } if (younger_gens_as_roots) { if (!_gen_process_strong_tasks->is_task_claimed(GCH_PS_younger_gens)) { for (int i = 0; i < level; i++) { not_older_gens->set_generation(_gens[i]); _gens[i]->oop_iterate(not_older_gens); } not_older_gens->reset_generation(); } } // When collection is parallel, all threads get to cooperate to do // older-gen scanning. for (int i = level+1; i < _n_gens; i++) { older_gens->set_generation(_gens[i]); rem_set()->younger_refs_iterate(_gens[i], older_gens); older_gens->reset_generation(); } younger_refs_in_space_iterate _gen_process_strong_tasks->all_tasks_completed();
最重要的三個點是
1.process_strong_roots 對根節點使用某個特定的 closure,上文中是 not_older_gens,即不是針對老年代的 Clsoure
2.第一個迴圈的oop_iterate 倘若是老年代呼叫 gen_process_strong_roots ,則level是1,也就是會呼叫迴圈中的本 oop_iterate , 年輕代的level 是 0,所以不會呼叫。本oop_iterate是使用 not_older_gens 的 closure 去處理oop的引用型別
3.第二個迴圈的younger_refs_iterate 是用來遍歷髒卡表的,上面用的是 older_gens 去遍歷髒卡表對應區域引用的物件
1.process_strong_roots
void SharedHeap::process_strong_roots(bool activate_scope, bool collecting_perm_gen, ScanningOption so, OopClosure* roots, CodeBlobClosure* code_roots, OopsInGenClosure* perm_blk) { StrongRootsScope srs(this, activate_scope); // General strong roots. assert(_strong_roots_parity != 0, "must have called prologue code"); if (!_process_strong_tasks->is_task_claimed(SH_PS_Universe_oops_do)) { Universe::oops_do(roots); //1 ReferenceProcessor::oops_do(roots); // Consider perm-gen discovered lists to be strong. perm_gen()->ref_processor()->weak_oops_do(roots); } // Global (strong) JNI handles if (!_process_strong_tasks->is_task_claimed(SH_PS_JNIHandles_oops_do)) JNIHandles::oops_do(roots); //2 // All threads execute this; the individual threads are task groups. if (ParallelGCThreads > 0) { Threads::possibly_parallel_oops_do(roots, code_roots); } else { Threads::oops_do(roots, code_roots); //3 } if (!_process_strong_tasks-> is_task_claimed(SH_PS_ObjectSynchronizer_oops_do)) ObjectSynchronizer::oops_do(roots); //4 if (!_process_strong_tasks->is_task_claimed(SH_PS_FlatProfiler_oops_do)) FlatProfiler::oops_do(roots); //5 if (!_process_strong_tasks->is_task_claimed(SH_PS_Management_oops_do)) Management::oops_do(roots); //6 if (!_process_strong_tasks->is_task_claimed(SH_PS_jvmti_oops_do)) JvmtiExport::oops_do(roots); //7 if (!_process_strong_tasks->is_task_claimed(SH_PS_SystemDictionary_oops_do)) { if (so & SO_AllClasses) { SystemDictionary::oops_do(roots); //8 } else if (so & SO_SystemClasses) { SystemDictionary::always_strong_oops_do(roots); } } if (!_process_strong_tasks->is_task_claimed(SH_PS_StringTable_oops_do)) { if (so & SO_Strings || (!collecting_perm_gen && !JavaObjectsInPerm)) { StringTable::oops_do(roots); //9 } if (JavaObjectsInPerm) { // Verify the string table contents are in the perm gen NOT_PRODUCT(StringTable::oops_do(&assert_is_perm_closure)); } } if (!_process_strong_tasks->is_task_claimed(SH_PS_CodeCache_oops_do)) { if (so & SO_CodeCache) { // (Currently, CMSCollector uses this to do intermediate-strength collections.) assert(collecting_perm_gen, "scanning all of code cache"); assert(code_roots != NULL, "must supply closure for code cache"); if (code_roots != NULL) { CodeCache::blobs_do(code_roots); } } else if (so & (SO_SystemClasses|SO_AllClasses)) { if (!collecting_perm_gen) { // If we are collecting from class statics, but we are not going to // visit all of the CodeCache, collect from the non-perm roots if any. // This makes the code cache function temporarily as a source of strong // roots for oops, until the next major collection. // // If collecting_perm_gen is true, we require that this phase will call // CodeCache::do_unloading. This will kill off nmethods with expired // weak references, such as stale invokedynamic targets. CodeCache::scavenge_root_nmethods_do(code_roots); } } // Verify that the code cache contents are not subject to // movement by a scavenging collection. DEBUG_ONLY(CodeBlobToOopClosure assert_code_is_non_scavengable(&assert_is_non_scavengable_closure, /*do_marking=*/ false)); DEBUG_ONLY(CodeCache::asserted_non_scavengable_nmethods_do(&assert_code_is_non_scavengable)); } if (!collecting_perm_gen) { // All threads perform this; coordination is handled internally. rem_set()->younger_refs_iterate(perm_gen(), perm_blk); } _process_strong_tasks->all_tasks_completed(); }
1.Universe : 某些全域性量持有的物件引用,比如:
mirror 是某個類靜態遍歷的集合,比如說有 Class A { static int a ; int b; static A c;}
那麼這個類的 mirror 就是 一個 12 位元組的 oop,其實只是 12 位元組的連續記憶體 區塊。其中存放了 4 位元組的 int 型別變數,8位元組的指標型別變數
上面這些 mirror 都是 java 提供的基礎型別 的類 的 靜態變數集合。每個集合 在 jvm 的 C++ 層面稱為 oopDesc 。 oop 是 oopDesc * , 即oopDesc 的指標
f 在當前語境下是 FastScanClosure,並且 處理 oop * 後不會把這個 oop * 的卡置為 youngercard,FastScanClosure的處理邏輯如下:把 oop * 的值引用的 oopDesc 複製到 to 或 老年代(年齡夠了的情況下)
注意 oop * 是 oopDesc 的指標的指標,也就是 oop * 是一個8位元組的記憶體地址,指向的地方的8位元組處是 oop,也是 一個 8 位元組的記憶體地址,指向的地方是一個 oopDesc
所以通過 f 修改 oop * 指向的記憶體區域,就是在修改某個 C++ 層面的類或結構(此處是 Universe)的指標域
關於 f :do_oop 會呼叫 do_oop_work
下面的邏輯是,如果物件沒有被複制到別的地方,也就是 forwardee 指標是空,就嘗試把物件複製到 to 區,或 promote 到 老年代
針對年輕代的 _gc_barrier 是 false
do_barrier 會把 oop * 在卡表中對應位元組(一個位元組代表一張卡)的值設定為youngercard
每種 generation 都有自己 copy_to_survivor_space 的實現,以下是預設年輕代的實現
oop DefNewGeneration::copy_to_survivor_space(oop old) { assert(is_in_reserved(old) && !old->is_forwarded(), "shouldn't be scavenging this oop"); size_t s = old->size(); oop obj = NULL; // Try allocating obj in to-space (unless too old) if (old->age() < tenuring_threshold()) { obj = (oop) to()->allocate(s); } // Otherwise try allocating obj tenured if (obj == NULL) { obj = _next_gen->promote(old, s); if (obj == NULL) { handle_promotion_failure(old); return old; } } else { // Prefetch beyond obj const intx interval = PrefetchCopyIntervalInBytes; Prefetch::write(obj, interval); // Copy obj Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)obj, s); // Increment age if obj still in new generation obj->incr_age(); age_table()->add(obj, s); } // Done, insert forward pointer to obj in this header old->forward_to(obj); return obj; }
上面的1~9就是所謂的根節點,其中比較重要的幾個是 Universe,java執行緒棧中的oop ,jni持有的 oop
3.younger_refs_iterate
這個方法會遍歷髒卡表項對應記憶體區的物件的引用,而且是針對老年代的。
比如老年代有個物件 A 持有一個引用 b 指向 物件 B,實際上, A 和 B 都是 oopDesc,b 在 c ++ 層面就是一個 oop,也就是 oopDesc *
當 A 的 b 引用 指向了其他物件,也就是 A.b = xxx , 那麼 A 的 b 此時會指向 xxx 這個 oopDesc,此時 A 對應記憶體區的卡 就是一片髒卡,對應的卡表項就是髒的。
所以要用 f 處理 A, 在處理之前會把 A 的記憶體對應卡表項置位乾淨卡。f 的入參是 A 的各個 oop 的地址
比如 A 在 C ++ 層面的記憶體表示是: