SlideShare a Scribd company logo
並列 GC 第 5 回 JVM ソースコードリーディングの会 (OpenJDK) 中村 実 [email_address] [email_address] Twitter @nminoru_jp
OpenJDK の GC の種類 同上 GenCollectedHeap -Xincgc ( コンカレント GC に -XX:+CMSIncrementalMode をつけたもの ) インクリメンタル GC G1CollectedHeap -XX:+UseG1GC G1GC ParNewGeneration ConcurrentMarkSweep GenCollectedHeap -XX:+UseConcMarkSweepGC コンカレント GC ParallelScavengeHeap -XX:+UseParallelGC 並列 GC ParNewGeneration TenuredGeneration GenCollectedHeap -XX:+UseParNewGC 並列 GC DefNewGeneration TenuredGeneration GenCollectedHeap -XX:+UseSerialGC 逐次 GC 空間のクラス ヒープのクラス オプション
OpenJDK6 の GC の種類 並列 GC は 2 種類ある 従来の SerialGC の NewGC を並列化した UseParNewGC 新世代と旧世代の配置を動的に変更し、 NewGC の並列化を行った UseParallelGC UseParallelGC は -XX:+UseParallelOldGC をつけることで Full GC の並列化も可能 アルゴリズム的にはほとんど違いがない 今日は UseParallelGC を読みます。
逐次 GC の簡単なおさらい 世代別 GC マイナー GC(New GC) はコピー GC メジャー GC(Full GC) はマークコンパクト GC
逐次 GC の簡単なおさらい 空間配置 ヒープは New, Oldm Perm の 3 つに分かれる。 New はさらに Eden, From, To に分かれる。 From と To は GC 時に切り替わる。 Perm Old Gen. Eden From To
逐次 GC の簡単なおさらい New GC Eden と From にあるオブジェクトのうち生きているものを To にコピーする。 生きているオブジェクトとは (a)  スタックや Old 世代から参照されていもの (b) (a) からさらに参照を受けているもの。 GC 後は Eden と From が空になる。 From と To はラベルを交換する。 Perm Old Gen. Eden From To
New GC の手順 Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/From への参照を持つオブジェクトを探す。 オブジェクトがまだコピーされていなければ、 To 領域にコピーし、移動先にポインタを張り替える オブジェクトがすでにコピーされていれば移動先にポインタを張り替える To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 c b a
New GC の手順 Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/From への参照を持つオブジェクトを探す。 オブジェクトがまだコピーされていなければ、 To 領域にコピーし、移動先にポインタを張り替える オブジェクトがすでにコピーされていれば移動先にポインタを張り替える To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 c b a b’
New GC の手順 Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/From への参照を持つオブジェクトを探す。 オブジェクトがまだコピーされていなければ、 To 領域にコピーし、移動先にポインタを張り替える オブジェクトがすでにコピーされていれば移動先にポインタを張り替える To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 c b a b’
NewGC のポイント Forwarding pointer コピー元のオブジェクトの oopDesc の _mark を潰してコピー先のアドレスを記録する。 (src/share/vm/oops/mark.hpp) すでに移動しているかどうかは LSB の 2 ビットを marked に変えて通知する。 オブジェクトの本体 _klass 2 1 4 hash code mark biased lock age
ソースコードで確認 src/share/vm/memory/defNewGeneartion.cpp:524 void DefNewGeneration::collect() { //  ルート (Old 領域を含む ) 内のオブジェクトをスキャンして Eden/From 空間 //  を指すオブジェクトを探す gch->gen_process_strong_roots(_level, // To  空間にコピーさえたオブジェクトをスキャンする evacuate_followers.do_void();
ソースコードで確認 DefNewGeneration::copy_to_survivor_space at src/share/vm/memory/defNewGeneartion.cpp:720 oop DefNewGeneration::copy_to_survivor_space(oop old) { size_t s = old->size(); oop obj = NULL; // TO  領域から空き容量を確保 obj = (oop) to()->allocate(s); //  コピー Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)obj, s); //  古い方を old->forward_to(obj); return obj; }
ソースコードで確認 DefNewGeneration::copy_to_survivor_space  at share/vm/memory/defNewGeneration.cpp:723 FastScanClosure::do_oop_work at share/vm/utilities/globalDefinitions.hpp:418 FastScanClosure::do_oop_nv at share/vm/runtime/thread.hpp:1826 objArrayKlass::oop_oop_iterate_nv at share/vm/oops/objArrayKlass.cpp:439 oopDesc::oop_iterate(FastScanClosure*) at share/vm/utilities/growableArray.hpp:204 ContiguousSpace::oop_since_save_marks_iterate_nv at share/vm/memory/space.cpp:784 OneContigSpaceCardGeneration::oop_since_save_marks_iterate_nv at share/vm/memory/generation.cpp:682 GenCollectedHeap::oop_since_save_marks_iterate at share/vm/memory/genCollectedHeap.cpp:786 DefNewGeneration::FastEvacuateFollowersClosure::do_void at share/vm/memory/defNewGeneration.cpp:112 DefNewGeneration::collect  at share/vm/memory/defNewGeneration.cpp:591 GenCollectedHeap::do_collection at share/vm/memory/genCollectedHeap.cpp:610 GenCollectorPolicy::satisfy_failed_allocation at share/vm/memory/collectorPolicy.cpp:694 GenCollectedHeap::satisfy_failed_allocation at share/vm/memory/genCollectedHeap.cpp:706 VM_GenCollectForAllocation::doit at share/vm/gc_implementation/shared/vmGCOperations.cpp:165 VM_Operation::evaluate at share/vm/runtime/vm_operations.cpp:65 VMThread::evaluate_operation at share/vm/runtime/vmThread.cpp:360 VMThread::loop at share/vm/runtime/vmThread.cpp:466 VMThread::run at share/vm/runtime/vmThread.cpp:273 java_start at os/linux/vm/os_linux.cpp:852 start_thread from /lib/libpthread.so.0 clone from /lib/libc.so.6
逐次 GC の簡単なおさらい Full GC ヒープ全体をマーキングして、生きているオブジェクトを Old に集める。 生きているオブジェクトが Old に入りきらない場合には各空間でコンパクションする Perm Old Gen. Eden From To
Full GC の手順 Phase1: marking スタックから参照されているオブジェクトをトレースしてマーキングする。 vm/gc_imimplementation/shared/markSweep.inline.hpp Phase2: compute new address Old 空間内をコンパクションした場合の移動先を計算し、 forwarding pointer として書き込む Phase3: adjust pointers オブジェクトの参照 ( ポインタ ) を移動先のアドレスに書き換える Phase4: compaction(move) 生きているオブジェクトだけを詰めて移動する。 GenMarkSweep::invoke_at_safepoint at src/share/vm/memory/genMarkSweep.cpp:59
ここから並列 GC の話
UseParallelGC GC スレッドの並列化 GCTaskThread はシステムの CPU 数に合わせて生成される ( -XX:ParallelGCThreads= n で変更可能 ) VMThread が GC を受けて、 GCTaskThread に処理を依頼する。 システムに一つのタスクキューに GCTask の派生型の処理を push すると、 GCTaskThread が pop して処理を開始する。 parallelScavenge/gcTaskThread.cpp:95
UseParallelGC VM_ParallelGCSystemGC::doit(hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp) の中で GC を発生させる。 // NewGC  の場合 ParallelScavengeHeap::invoke_scavenge PSScavenge::invoke() -> PSScavenge::invoke_no_policy ( 本体 ) // Full GC  の場合 ParallelScavengeHeap::invoke_full_gc if (UseParallelOldGC)  PSParallelCompact::invoke ->  PSParallelCompact::invoke_no_policy ( 本体 ) else PSMarkSweep::invoke
Parallel Scavange New GC の並列版 New GC のほぼ全処理が並列化される ランデブーポイントは 1 箇所 LAB オブジェクトを TO 領域にコピーする際に、 TLAB のようにあらかじめ一定範囲を GCTaskThread に割り当てる機構。アロケートのためのロックを減らせるのと、キャッシュの false sharing を防げる。 PSPromotionManager GC スレッド毎にインスタンスがあり、マーキングスタックや LAB を管理。 自分の仕事を先に済ませた GC スレッドは、他の GC スレッドの担当分を盗める (steal) ようになている。 OldToYoungRootsTask Old 領域は複数の GC スレッドで処理するため 128 バイト単位でストライプ状に担当領域を分ける。
Parallel Scavange Parallel Scavange のスタートポイント PSScavenge::invoke_no_policy()  のスタックトレース #0  PSScavenge::invoke_no_policy  at parallelScavenge/psScavenge.cpp:250 #1  PSParallelCompact::invoke  at parallelScavenge/psParallelCompact.cpp:1980 #2  ParallelScavengeHeap::invoke_full_gc  at share/vm/utilities/growableArray.hpp:204 #3  VM_ParallelGCSystemGC::doit  at parallelScavenge/vmPSOperations.cpp:101 #4  VM_Operation::evaluate  at share/vm/runtime/vm_operations.cpp:65 #5  VMThread::evaluate_operation  at share/vm/runtime/vmThread.cpp:360 #6  VMThread::loop  at share/vm/runtime/vmThread.cpp:466 #7  VMThread::run  at /share/vm/runtime/vmThread.cpp:273 #8  java_start  at os/linux/vm/os_linux.cpp:852 #9  start_thread  from /lib/libpthread.so.0 #10  clone  from /lib/libc.so.6
Parallel Scavange Old 世代のスキャン関数 CardTableExtension::scavenge_contents_parallel #0  CardTableExtension::scavenge_contents_parallel  at parallelScavenge/cardTableExtension.cpp:227 #1  OldToYoungRootsTask::do_it  at parallelScavenge/psTasks.cpp:224 #2  GCTaskThread::run  at parallelScavenge/gcTaskThread.cpp:135 #3  java_start  at os/linux/vm/os_linux.cpp:852 #4  start_thread  from /lib/libpthread.so.0 #5  clone  from /lib/libc.so.6
Parallel Scavange 一番肝になる関数  PSPromotionManager::copy_to_survivor_space  が呼び出されるまでのスタックトレース #0  PSPromotionManager::copy_to_survivor_space  at parallelScavenge/psPromotionManager.cpp:259 #1  PSScavenge::copy_and_push_safe_barrier  at vm/utilities/growableArray.hpp:204 #2  PSScavengeRootsClosure::do_oop_work   at vm/utilities/growableArray.hpp:204 #3  PSScavengeRootsClosure::do_oop  at vm/utilities/growableArray.hpp:204 #4  Universe::oops_do  at vm/memory/universe.cpp:262 #5  ScavengeRootsTask::do_it  at parallelScavenge/psTasks.cpp:75 #6  GCTaskThread::run  at parallelScavenge/gcTaskThread.cpp:135 #7  java_start  at os/linux/vm/os_linux.cpp:852 #8  start_thread  from /lib/libpthread.so.0 #9  clone  from /lib/libc.so.6
Parallel Scavange oop PSPromotionManager::copy_to_survivor_space(oop o) { oop new_obj = NULL; markOop test_mark = o->mark(); if (!test_mark->is_marked()) { //  このオブジェクトはまだコピーされていない。 // LAB  からコピー先確保 new_obj = (oop) _young_lab.allocate(new_obj_size); // new_obj  が  NULL  なら例外処理 Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)new_obj, new_obj_size); // Compare-and-swap  でコピー元オブジェクトに  foward pointer  を設定 if (o->cas_forward_to(new_obj, test_mark)) { new_obj->push_contents(this); }  else { // CAS  に失敗した場合、コピーしたオブジェクトは無駄になった。 // unallocate_object(new_obj)  で  LAB  に返す。 new_obj = o->forwardee(); } } else new_obj = o->forwardee(); //  誰かが既にマークしている場合 return new_obj; }
Parallel Scavange タスクスチール マーキングスタックは share/vm/utilities/taskqueue.hpp のデータ構造でできている。これはロックフリーデータ構造 http ://www.nminoru.jp/~nminoru/programming/arora_dequeue.html StealTask::StealTask - TaskQueueSuper<N> - GenericTaskQueue<E, N> - OverflowTaskQueue<E, N> - OopStarTaskQueue
Parallel Compact Full GC の並列版 Mark & compact Bitmap marking を行っている 移動先のデータを forwarding pointer を使わずに、 ParallelCompactData に記録する 手順が少し異なる Phase 1: marking(parallel) Phase 2: summary(serial) compute new address に相当 Phase 3: adjust roots(serial) スタック等の参照を変更する。ヒープ上のオブジェクトの pointer adjust は行わない。 Phase 4: compact perm(serial) Phaes 5: compact(parallel)
Parallel Compact Parallel Compact のスタートポイント PSScavenge::invoke_no_policy Full のスタックトレース #0  PSScavenge::invoke_no_policy  at parallelScavenge/psScavenge.cpp:250 #1  PSParallelCompact::invoke  at parallelScavenge/psParallelCompact.cpp:1980 #2  ParallelScavengeHeap::invoke_full_gc  at share/vm/utilities/growableArray.hpp:204 #3  VM_ParallelGCSystemGC::doit  at parallelScavenge/vmPSOperations.cpp:101 #4  VM_Operation::evaluate  at share/vm/runtime/vm_operations.cpp:65 #5  VMThread::evaluate_operation  at share/vm/runtime/vmThread.cpp:360 #6  VMThread::loop  at share/vm/runtime/vmThread.cpp:466 #7  VMThread::run  at share/vm/runtime/vmThread.cpp:273 #8  java_start  at os/linux/vm/os_linux.cpp:852 #9  start_thread  from /lib/libpthread.so.0 #10  clone  from /lib/libc.so.6
Parallel Compact Phase1 marking で未マークなオブジェクトを見つけて PerMarkBitMap を打つところまでのスタックトレース #0  ParMarkBitMap::mark_obj  at parallelScavenge/parMarkBitMap.cpp:92 #1  ParMarkBitMap::mark_obj  at share/vm/memory/cardTableRS.hpp:150 #2  PSParallelCompact::mark_obj  at share/vm/memory/cardTableRS.hpp:150 #3  PSParallelCompact::mark_and_push  at share/vm/memory/cardTableRS.hpp:150 #4  PSParallelCompact::MarkAndPushClosure::do_oop  at parallelScavenge/psParallelCompact.cpp:822 #5  JNIHandles::oops_do  at share/vm/runtime/jniHandles.cpp:146 #6  MarkFromRootsTask::do_it  at parallelScavenge/pcTasks.cpp:88 #7  GCTaskThread::run  at parallelScavenge/gcTaskThread.cpp:135 #8  java_start  at os/linux/vm/os_linux.cpp:852 #9  start_thread  from /lib/libpthread.so.0 #10  clone  from /lib/libc.so.6
Parallel Compact Phase1 marking で未マークなオブジェクトを見つけて PerMarkBitMap を打つところまでのスタックトレース #0  ParMarkBitMap::mark_obj  at parallelScavenge/parMarkBitMap.cpp:92 #1  ParMarkBitMap::mark_obj  at share/vm/memory/cardTableRS.hpp:150 #2  PSParallelCompact::mark_obj  at share/vm/memory/cardTableRS.hpp:150 #3  PSParallelCompact::mark_and_push  at share/vm/memory/cardTableRS.hpp:150 #4  PSParallelCompact::MarkAndPushClosure::do_oop  at parallelScavenge/psParallelCompact.cpp:822 #5  JNIHandles::oops_do  at share/vm/runtime/jniHandles.cpp:146 #6  MarkFromRootsTask::do_it  at parallelScavenge/pcTasks.cpp:88 #7  GCTaskThread::run  at parallelScavenge/gcTaskThread.cpp:135 #8  java_start  at os/linux/vm/os_linux.cpp:852 #9  start_thread  from /lib/libpthread.so.0 #10  clone  from /lib/libc.so.6
Parallel Compact Phase3 adjust roots PSParallelCompact::adjust_pointer のスタックトレース #0  PSParallelCompact::adjust_pointer  at  parallelScavenge/psParallelCompact.hpp:1318 #1  PSParallelCompact::AdjustPointerClosure::do_oop  at parallelScavenge/psParallelCompact.cpp:817 #2  Universe::oops_do  at share/vm/memory/universe.cpp:208 #3  PSParallelCompact::adjust_roots  at parallelScavenge/psParallelCompact.cpp:2449 #4  PSParallelCompact::invoke_no_policy  at parallelScavenge/psParallelCompact.cpp:2098 #5  PSParallelCompact::invoke  at parallelScavenge/psParallelCompact.cpp:1987 #6  ParallelScavengeHeap::invoke_full_gc  at share/vm/utilities/growableArray.hpp:204 #7  VM_ParallelGCSystemGC::doit  at parallelScavenge/vmPSOperations.cpp:101 ...
Parallel Compact PSParallelCompact::adjust_pointer at  parallelScavenge/psParallelCompact.hpp:1318 template <class T> inline void PSParallelCompact::adjust_pointer(T* p, bool isroot) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop)) { oop obj  = oopDesc::decode_heap_oop_not_null(heap_oop); oop new_obj =  (oop)summary_data().calc_new_pointer(obj);  //  移動先の取得 // Just always do the update unconditionally? if (new_obj != NULL) { oopDesc::encode_store_heap_oop_not_null(p, new_obj); //  ポインタ変更 }  } }
Parallel Compact Phase4 #0  PSParallelCompact::adjust_pointer  at  parallelScavenge/psParallelCompact.hpp:1318 #1  PSParallelCompact::adjust_pointer at share/vm/memory/cardTableRS.hpp:150 #2  klassKlass::oop_update_pointers at share/vm/oops/klassKlass.cpp:193 #3  oopDesc::update_contents at share/vm/utilities/growableArray.hpp:204 #4  UpdateOnlyClosure::do_addr at  parallelScavenge/psParallelCompact.hpp:1498 #5  UpdateOnlyClosure::do_addr at parallelScavenge/psParallelCompact.cpp:3514 #6  ParMarkBitMap::iterate at parallelScavenge/parMarkBitMap.cpp:219 #7  ParMarkBitMap::iterate at share/vm/utilities/growableArray.hpp:204 #8  PSParallelCompact::update_and_deadwood_in_dense_prefix at parallelScavenge/psParallelCompact.cpp:3001 #9  PSParallelCompact::move_and_update  at parallelScavenge/psParallelCompact.cpp:3404 #10 PSParallelCompact::compact_perm at sparallelScavenge/psParallelCompact.cpp:2484 #11 PSParallelCompact::invoke_no_policy at parallelScavenge/psParallelCompact.cpp:2103
Parallel Compact Phase5 #0  PSParallelCompact::adjust_pointer  at share/vm/memory/cardTableRS.hpp:150 #1  PSParallelCompact::adjust_pointer  at share/vm/memory/cardTableRS.hpp:150 #2  instanceKlass::oop_update_pointers  at share/vm/oops/instanceKlass.cpp:1870 #3  oopDesc::update_contents  at share/vm/utilities/growableArray.hpp:204 #4  MoveAndUpdateClosure::do_addr  at parallelScavenge/psParallelCompact.cpp:3494 #5  ParMarkBitMap::iterate  at parallelScavenge/parMarkBitMap.cpp:171 #6  ParMarkBitMap::iterate  at share/vm/utilities/growableArray.hpp:204 #7  PSParallelCompact::fill_region  at parallelScavenge/psParallelCompact.cpp:3323 #8  PSParallelCompact::fill_and_update_region  at share/vm/utilities/growableArray.hpp:204 #9  ParCompactionManager::drain_region_stacks  at parallelScavenge/psCompactionManager.cpp:172 #10  DrainStacksCompactionTask::do_it  at parallelScavenge/pcTasks.cpp:297 #11  GCTaskThread::run  at parallelScavenge/gcTaskThread.cpp:135 ...

More Related Content

JVM-Reading-ParalleGC

  • 1. 並列 GC 第 5 回 JVM ソースコードリーディングの会 (OpenJDK) 中村 実 [email_address] [email_address] Twitter @nminoru_jp
  • 2. OpenJDK の GC の種類 同上 GenCollectedHeap -Xincgc ( コンカレント GC に -XX:+CMSIncrementalMode をつけたもの ) インクリメンタル GC G1CollectedHeap -XX:+UseG1GC G1GC ParNewGeneration ConcurrentMarkSweep GenCollectedHeap -XX:+UseConcMarkSweepGC コンカレント GC ParallelScavengeHeap -XX:+UseParallelGC 並列 GC ParNewGeneration TenuredGeneration GenCollectedHeap -XX:+UseParNewGC 並列 GC DefNewGeneration TenuredGeneration GenCollectedHeap -XX:+UseSerialGC 逐次 GC 空間のクラス ヒープのクラス オプション
  • 3. OpenJDK6 の GC の種類 並列 GC は 2 種類ある 従来の SerialGC の NewGC を並列化した UseParNewGC 新世代と旧世代の配置を動的に変更し、 NewGC の並列化を行った UseParallelGC UseParallelGC は -XX:+UseParallelOldGC をつけることで Full GC の並列化も可能 アルゴリズム的にはほとんど違いがない 今日は UseParallelGC を読みます。
  • 4. 逐次 GC の簡単なおさらい 世代別 GC マイナー GC(New GC) はコピー GC メジャー GC(Full GC) はマークコンパクト GC
  • 5. 逐次 GC の簡単なおさらい 空間配置 ヒープは New, Oldm Perm の 3 つに分かれる。 New はさらに Eden, From, To に分かれる。 From と To は GC 時に切り替わる。 Perm Old Gen. Eden From To
  • 6. 逐次 GC の簡単なおさらい New GC Eden と From にあるオブジェクトのうち生きているものを To にコピーする。 生きているオブジェクトとは (a) スタックや Old 世代から参照されていもの (b) (a) からさらに参照を受けているもの。 GC 後は Eden と From が空になる。 From と To はラベルを交換する。 Perm Old Gen. Eden From To
  • 7. New GC の手順 Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/From への参照を持つオブジェクトを探す。 オブジェクトがまだコピーされていなければ、 To 領域にコピーし、移動先にポインタを張り替える オブジェクトがすでにコピーされていれば移動先にポインタを張り替える To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 c b a
  • 8. New GC の手順 Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/From への参照を持つオブジェクトを探す。 オブジェクトがまだコピーされていなければ、 To 領域にコピーし、移動先にポインタを張り替える オブジェクトがすでにコピーされていれば移動先にポインタを張り替える To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 c b a b’
  • 9. New GC の手順 Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/From への参照を持つオブジェクトを探す。 オブジェクトがまだコピーされていなければ、 To 領域にコピーし、移動先にポインタを張り替える オブジェクトがすでにコピーされていれば移動先にポインタを張り替える To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 c b a b’
  • 10. NewGC のポイント Forwarding pointer コピー元のオブジェクトの oopDesc の _mark を潰してコピー先のアドレスを記録する。 (src/share/vm/oops/mark.hpp) すでに移動しているかどうかは LSB の 2 ビットを marked に変えて通知する。 オブジェクトの本体 _klass 2 1 4 hash code mark biased lock age
  • 11. ソースコードで確認 src/share/vm/memory/defNewGeneartion.cpp:524 void DefNewGeneration::collect() { // ルート (Old 領域を含む ) 内のオブジェクトをスキャンして Eden/From 空間 // を指すオブジェクトを探す gch->gen_process_strong_roots(_level, // To 空間にコピーさえたオブジェクトをスキャンする evacuate_followers.do_void();
  • 12. ソースコードで確認 DefNewGeneration::copy_to_survivor_space at src/share/vm/memory/defNewGeneartion.cpp:720 oop DefNewGeneration::copy_to_survivor_space(oop old) { size_t s = old->size(); oop obj = NULL; // TO 領域から空き容量を確保 obj = (oop) to()->allocate(s); // コピー Copy::aligned_disjoint_words((HeapWord*)old, (HeapWord*)obj, s); // 古い方を old->forward_to(obj); return obj; }
  • 13. ソースコードで確認 DefNewGeneration::copy_to_survivor_space at share/vm/memory/defNewGeneration.cpp:723 FastScanClosure::do_oop_work at share/vm/utilities/globalDefinitions.hpp:418 FastScanClosure::do_oop_nv at share/vm/runtime/thread.hpp:1826 objArrayKlass::oop_oop_iterate_nv at share/vm/oops/objArrayKlass.cpp:439 oopDesc::oop_iterate(FastScanClosure*) at share/vm/utilities/growableArray.hpp:204 ContiguousSpace::oop_since_save_marks_iterate_nv at share/vm/memory/space.cpp:784 OneContigSpaceCardGeneration::oop_since_save_marks_iterate_nv at share/vm/memory/generation.cpp:682 GenCollectedHeap::oop_since_save_marks_iterate at share/vm/memory/genCollectedHeap.cpp:786 DefNewGeneration::FastEvacuateFollowersClosure::do_void at share/vm/memory/defNewGeneration.cpp:112 DefNewGeneration::collect at share/vm/memory/defNewGeneration.cpp:591 GenCollectedHeap::do_collection at share/vm/memory/genCollectedHeap.cpp:610 GenCollectorPolicy::satisfy_failed_allocation at share/vm/memory/collectorPolicy.cpp:694 GenCollectedHeap::satisfy_failed_allocation at share/vm/memory/genCollectedHeap.cpp:706 VM_GenCollectForAllocation::doit at share/vm/gc_implementation/shared/vmGCOperations.cpp:165 VM_Operation::evaluate at share/vm/runtime/vm_operations.cpp:65 VMThread::evaluate_operation at share/vm/runtime/vmThread.cpp:360 VMThread::loop at share/vm/runtime/vmThread.cpp:466 VMThread::run at share/vm/runtime/vmThread.cpp:273 java_start at os/linux/vm/os_linux.cpp:852 start_thread from /lib/libpthread.so.0 clone from /lib/libc.so.6
  • 14. 逐次 GC の簡単なおさらい Full GC ヒープ全体をマーキングして、生きているオブジェクトを Old に集める。 生きているオブジェクトが Old に入りきらない場合には各空間でコンパクションする Perm Old Gen. Eden From To
  • 15. Full GC の手順 Phase1: marking スタックから参照されているオブジェクトをトレースしてマーキングする。 vm/gc_imimplementation/shared/markSweep.inline.hpp Phase2: compute new address Old 空間内をコンパクションした場合の移動先を計算し、 forwarding pointer として書き込む Phase3: adjust pointers オブジェクトの参照 ( ポインタ ) を移動先のアドレスに書き換える Phase4: compaction(move) 生きているオブジェクトだけを詰めて移動する。 GenMarkSweep::invoke_at_safepoint at src/share/vm/memory/genMarkSweep.cpp:59
  • 17. UseParallelGC GC スレッドの並列化 GCTaskThread はシステムの CPU 数に合わせて生成される ( -XX:ParallelGCThreads= n で変更可能 ) VMThread が GC を受けて、 GCTaskThread に処理を依頼する。 システムに一つのタスクキューに GCTask の派生型の処理を push すると、 GCTaskThread が pop して処理を開始する。 parallelScavenge/gcTaskThread.cpp:95
  • 18. UseParallelGC VM_ParallelGCSystemGC::doit(hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp) の中で GC を発生させる。 // NewGC の場合 ParallelScavengeHeap::invoke_scavenge PSScavenge::invoke() -> PSScavenge::invoke_no_policy ( 本体 ) // Full GC の場合 ParallelScavengeHeap::invoke_full_gc if (UseParallelOldGC) PSParallelCompact::invoke -> PSParallelCompact::invoke_no_policy ( 本体 ) else PSMarkSweep::invoke
  • 19. Parallel Scavange New GC の並列版 New GC のほぼ全処理が並列化される ランデブーポイントは 1 箇所 LAB オブジェクトを TO 領域にコピーする際に、 TLAB のようにあらかじめ一定範囲を GCTaskThread に割り当てる機構。アロケートのためのロックを減らせるのと、キャッシュの false sharing を防げる。 PSPromotionManager GC スレッド毎にインスタンスがあり、マーキングスタックや LAB を管理。 自分の仕事を先に済ませた GC スレッドは、他の GC スレッドの担当分を盗める (steal) ようになている。 OldToYoungRootsTask Old 領域は複数の GC スレッドで処理するため 128 バイト単位でストライプ状に担当領域を分ける。
  • 20. Parallel Scavange Parallel Scavange のスタートポイント PSScavenge::invoke_no_policy() のスタックトレース #0 PSScavenge::invoke_no_policy at parallelScavenge/psScavenge.cpp:250 #1 PSParallelCompact::invoke at parallelScavenge/psParallelCompact.cpp:1980 #2 ParallelScavengeHeap::invoke_full_gc at share/vm/utilities/growableArray.hpp:204 #3 VM_ParallelGCSystemGC::doit at parallelScavenge/vmPSOperations.cpp:101 #4 VM_Operation::evaluate at share/vm/runtime/vm_operations.cpp:65 #5 VMThread::evaluate_operation at share/vm/runtime/vmThread.cpp:360 #6 VMThread::loop at share/vm/runtime/vmThread.cpp:466 #7 VMThread::run at /share/vm/runtime/vmThread.cpp:273 #8 java_start at os/linux/vm/os_linux.cpp:852 #9 start_thread from /lib/libpthread.so.0 #10 clone from /lib/libc.so.6
  • 21. Parallel Scavange Old 世代のスキャン関数 CardTableExtension::scavenge_contents_parallel #0 CardTableExtension::scavenge_contents_parallel at parallelScavenge/cardTableExtension.cpp:227 #1 OldToYoungRootsTask::do_it at parallelScavenge/psTasks.cpp:224 #2 GCTaskThread::run at parallelScavenge/gcTaskThread.cpp:135 #3 java_start at os/linux/vm/os_linux.cpp:852 #4 start_thread from /lib/libpthread.so.0 #5 clone from /lib/libc.so.6
  • 22. Parallel Scavange 一番肝になる関数 PSPromotionManager::copy_to_survivor_space が呼び出されるまでのスタックトレース #0 PSPromotionManager::copy_to_survivor_space at parallelScavenge/psPromotionManager.cpp:259 #1 PSScavenge::copy_and_push_safe_barrier at vm/utilities/growableArray.hpp:204 #2 PSScavengeRootsClosure::do_oop_work at vm/utilities/growableArray.hpp:204 #3 PSScavengeRootsClosure::do_oop at vm/utilities/growableArray.hpp:204 #4 Universe::oops_do at vm/memory/universe.cpp:262 #5 ScavengeRootsTask::do_it at parallelScavenge/psTasks.cpp:75 #6 GCTaskThread::run at parallelScavenge/gcTaskThread.cpp:135 #7 java_start at os/linux/vm/os_linux.cpp:852 #8 start_thread from /lib/libpthread.so.0 #9 clone from /lib/libc.so.6
  • 23. Parallel Scavange oop PSPromotionManager::copy_to_survivor_space(oop o) { oop new_obj = NULL; markOop test_mark = o->mark(); if (!test_mark->is_marked()) { // このオブジェクトはまだコピーされていない。 // LAB からコピー先確保 new_obj = (oop) _young_lab.allocate(new_obj_size); // new_obj が NULL なら例外処理 Copy::aligned_disjoint_words((HeapWord*)o, (HeapWord*)new_obj, new_obj_size); // Compare-and-swap でコピー元オブジェクトに foward pointer を設定 if (o->cas_forward_to(new_obj, test_mark)) { new_obj->push_contents(this); } else { // CAS に失敗した場合、コピーしたオブジェクトは無駄になった。 // unallocate_object(new_obj) で LAB に返す。 new_obj = o->forwardee(); } } else new_obj = o->forwardee(); // 誰かが既にマークしている場合 return new_obj; }
  • 24. Parallel Scavange タスクスチール マーキングスタックは share/vm/utilities/taskqueue.hpp のデータ構造でできている。これはロックフリーデータ構造 http ://www.nminoru.jp/~nminoru/programming/arora_dequeue.html StealTask::StealTask - TaskQueueSuper<N> - GenericTaskQueue<E, N> - OverflowTaskQueue<E, N> - OopStarTaskQueue
  • 25. Parallel Compact Full GC の並列版 Mark & compact Bitmap marking を行っている 移動先のデータを forwarding pointer を使わずに、 ParallelCompactData に記録する 手順が少し異なる Phase 1: marking(parallel) Phase 2: summary(serial) compute new address に相当 Phase 3: adjust roots(serial) スタック等の参照を変更する。ヒープ上のオブジェクトの pointer adjust は行わない。 Phase 4: compact perm(serial) Phaes 5: compact(parallel)
  • 26. Parallel Compact Parallel Compact のスタートポイント PSScavenge::invoke_no_policy Full のスタックトレース #0 PSScavenge::invoke_no_policy at parallelScavenge/psScavenge.cpp:250 #1 PSParallelCompact::invoke at parallelScavenge/psParallelCompact.cpp:1980 #2 ParallelScavengeHeap::invoke_full_gc at share/vm/utilities/growableArray.hpp:204 #3 VM_ParallelGCSystemGC::doit at parallelScavenge/vmPSOperations.cpp:101 #4 VM_Operation::evaluate at share/vm/runtime/vm_operations.cpp:65 #5 VMThread::evaluate_operation at share/vm/runtime/vmThread.cpp:360 #6 VMThread::loop at share/vm/runtime/vmThread.cpp:466 #7 VMThread::run at share/vm/runtime/vmThread.cpp:273 #8 java_start at os/linux/vm/os_linux.cpp:852 #9 start_thread from /lib/libpthread.so.0 #10 clone from /lib/libc.so.6
  • 27. Parallel Compact Phase1 marking で未マークなオブジェクトを見つけて PerMarkBitMap を打つところまでのスタックトレース #0 ParMarkBitMap::mark_obj at parallelScavenge/parMarkBitMap.cpp:92 #1 ParMarkBitMap::mark_obj at share/vm/memory/cardTableRS.hpp:150 #2 PSParallelCompact::mark_obj at share/vm/memory/cardTableRS.hpp:150 #3 PSParallelCompact::mark_and_push at share/vm/memory/cardTableRS.hpp:150 #4 PSParallelCompact::MarkAndPushClosure::do_oop at parallelScavenge/psParallelCompact.cpp:822 #5 JNIHandles::oops_do at share/vm/runtime/jniHandles.cpp:146 #6 MarkFromRootsTask::do_it at parallelScavenge/pcTasks.cpp:88 #7 GCTaskThread::run at parallelScavenge/gcTaskThread.cpp:135 #8 java_start at os/linux/vm/os_linux.cpp:852 #9 start_thread from /lib/libpthread.so.0 #10 clone from /lib/libc.so.6
  • 28. Parallel Compact Phase1 marking で未マークなオブジェクトを見つけて PerMarkBitMap を打つところまでのスタックトレース #0 ParMarkBitMap::mark_obj at parallelScavenge/parMarkBitMap.cpp:92 #1 ParMarkBitMap::mark_obj at share/vm/memory/cardTableRS.hpp:150 #2 PSParallelCompact::mark_obj at share/vm/memory/cardTableRS.hpp:150 #3 PSParallelCompact::mark_and_push at share/vm/memory/cardTableRS.hpp:150 #4 PSParallelCompact::MarkAndPushClosure::do_oop at parallelScavenge/psParallelCompact.cpp:822 #5 JNIHandles::oops_do at share/vm/runtime/jniHandles.cpp:146 #6 MarkFromRootsTask::do_it at parallelScavenge/pcTasks.cpp:88 #7 GCTaskThread::run at parallelScavenge/gcTaskThread.cpp:135 #8 java_start at os/linux/vm/os_linux.cpp:852 #9 start_thread from /lib/libpthread.so.0 #10 clone from /lib/libc.so.6
  • 29. Parallel Compact Phase3 adjust roots PSParallelCompact::adjust_pointer のスタックトレース #0 PSParallelCompact::adjust_pointer at parallelScavenge/psParallelCompact.hpp:1318 #1 PSParallelCompact::AdjustPointerClosure::do_oop at parallelScavenge/psParallelCompact.cpp:817 #2 Universe::oops_do at share/vm/memory/universe.cpp:208 #3 PSParallelCompact::adjust_roots at parallelScavenge/psParallelCompact.cpp:2449 #4 PSParallelCompact::invoke_no_policy at parallelScavenge/psParallelCompact.cpp:2098 #5 PSParallelCompact::invoke at parallelScavenge/psParallelCompact.cpp:1987 #6 ParallelScavengeHeap::invoke_full_gc at share/vm/utilities/growableArray.hpp:204 #7 VM_ParallelGCSystemGC::doit at parallelScavenge/vmPSOperations.cpp:101 ...
  • 30. Parallel Compact PSParallelCompact::adjust_pointer at parallelScavenge/psParallelCompact.hpp:1318 template <class T> inline void PSParallelCompact::adjust_pointer(T* p, bool isroot) { T heap_oop = oopDesc::load_heap_oop(p); if (!oopDesc::is_null(heap_oop)) { oop obj = oopDesc::decode_heap_oop_not_null(heap_oop); oop new_obj = (oop)summary_data().calc_new_pointer(obj); // 移動先の取得 // Just always do the update unconditionally? if (new_obj != NULL) { oopDesc::encode_store_heap_oop_not_null(p, new_obj); // ポインタ変更 } } }
  • 31. Parallel Compact Phase4 #0 PSParallelCompact::adjust_pointer at parallelScavenge/psParallelCompact.hpp:1318 #1 PSParallelCompact::adjust_pointer at share/vm/memory/cardTableRS.hpp:150 #2 klassKlass::oop_update_pointers at share/vm/oops/klassKlass.cpp:193 #3 oopDesc::update_contents at share/vm/utilities/growableArray.hpp:204 #4 UpdateOnlyClosure::do_addr at parallelScavenge/psParallelCompact.hpp:1498 #5 UpdateOnlyClosure::do_addr at parallelScavenge/psParallelCompact.cpp:3514 #6 ParMarkBitMap::iterate at parallelScavenge/parMarkBitMap.cpp:219 #7 ParMarkBitMap::iterate at share/vm/utilities/growableArray.hpp:204 #8 PSParallelCompact::update_and_deadwood_in_dense_prefix at parallelScavenge/psParallelCompact.cpp:3001 #9 PSParallelCompact::move_and_update at parallelScavenge/psParallelCompact.cpp:3404 #10 PSParallelCompact::compact_perm at sparallelScavenge/psParallelCompact.cpp:2484 #11 PSParallelCompact::invoke_no_policy at parallelScavenge/psParallelCompact.cpp:2103
  • 32. Parallel Compact Phase5 #0 PSParallelCompact::adjust_pointer at share/vm/memory/cardTableRS.hpp:150 #1 PSParallelCompact::adjust_pointer at share/vm/memory/cardTableRS.hpp:150 #2 instanceKlass::oop_update_pointers at share/vm/oops/instanceKlass.cpp:1870 #3 oopDesc::update_contents at share/vm/utilities/growableArray.hpp:204 #4 MoveAndUpdateClosure::do_addr at parallelScavenge/psParallelCompact.cpp:3494 #5 ParMarkBitMap::iterate at parallelScavenge/parMarkBitMap.cpp:171 #6 ParMarkBitMap::iterate at share/vm/utilities/growableArray.hpp:204 #7 PSParallelCompact::fill_region at parallelScavenge/psParallelCompact.cpp:3323 #8 PSParallelCompact::fill_and_update_region at share/vm/utilities/growableArray.hpp:204 #9 ParCompactionManager::drain_region_stacks at parallelScavenge/psCompactionManager.cpp:172 #10 DrainStacksCompactionTask::do_it at parallelScavenge/pcTasks.cpp:297 #11 GCTaskThread::run at parallelScavenge/gcTaskThread.cpp:135 ...