Download - Jvm reading-parallel gc
2
OpenJDK の GC の種類オプション ヒープのクラス 空間のクラス
逐次 GC -XX:+UseSerialGC GenCollectedHeap DefNewGeneration
TenuredGeneration
並列 GC -XX:+UseParNewGC GenCollectedHeap ParNewGeneration
TenuredGeneration
並列 GC -XX:+UseParallelGC ParallelScavengeHeap
コンカレントGC
-XX:+UseConcMarkSweepGC GenCollectedHeap ParNewGeneration
ConcurrentMarkSweep
インクリメンタル GC
-Xincgc( コンカレント GC に -XX:+CMSIncrementalMode をつけたもの )
GenCollectedHeap 同上
G1GC -XX:+UseG1GC G1CollectedHeap
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 の手順
1. Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/Fromへの参照を持つオブジェクトを探す。1. オブジェクトがまだコピーされていなけ
れば、 To 領域にコピーし、移動先にポインタを張り替える
2. オブジェクトがすでにコピーされていれば移動先にポインタを張り替える
2. To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 c
b
a
8
New GC の手順
1. Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/Fromへの参照を持つオブジェクトを探す。1. オブジェクトがまだコピーされていなけ
れば、 To 領域にコピーし、移動先にポインタを張り替える
2. オブジェクトがすでにコピーされていれば移動先にポインタを張り替える
2. To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 c
b
a
b’
9
New GC の手順
1. Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/Fromへの参照を持つオブジェクトを探す。1. オブジェクトがまだコピーされていなけ
れば、 To 領域にコピーし、移動先にポインタを張り替える
2. オブジェクトがすでにコピーされていれば移動先にポインタを張り替える
2. To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 c
b
a
b’
10
NewGC のポイント• Forwarding pointer
– コピー元のオブジェクトの oopDesc の _mark を潰してコピー先のアドレスを記録する。(src/share/vm/oops/mark.hpp)
– すでに移動しているかどうかは LSB の 2 ビットを marked に変えて通知する。
オブジェクトの本体
_klass214hash code
markbiased lockage
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:723FastScanClosure::do_oop_work at share/vm/utilities/globalDefinitions.hpp:418FastScanClosure::do_oop_nv at share/vm/runtime/thread.hpp:1826objArrayKlass::oop_oop_iterate_nv at share/vm/oops/objArrayKlass.cpp:439oopDesc::oop_iterate(FastScanClosure*) at share/vm/utilities/growableArray.hpp:204ContiguousSpace::oop_since_save_marks_iterate_nv at share/vm/memory/space.cpp:784OneContigSpaceCardGeneration::oop_since_save_marks_iterate_nv at share/vm/memory/generation.cpp:682GenCollectedHeap::oop_since_save_marks_iterate at share/vm/memory/genCollectedHeap.cpp:786DefNewGeneration::FastEvacuateFollowersClosure::do_void at share/vm/memory/defNewGeneration.cpp:112DefNewGeneration::collect at share/vm/memory/defNewGeneration.cpp:591GenCollectedHeap::do_collection at share/vm/memory/genCollectedHeap.cpp:610GenCollectorPolicy::satisfy_failed_allocation at share/vm/memory/collectorPolicy.cpp:694GenCollectedHeap::satisfy_failed_allocation at share/vm/memory/genCollectedHeap.cpp:706VM_GenCollectForAllocation::doit at share/vm/gc_implementation/shared/vmGCOperations.cpp:165VM_Operation::evaluate at share/vm/runtime/vm_operations.cpp:65VMThread::evaluate_operation at share/vm/runtime/vmThread.cpp:360VMThread::loop at share/vm/runtime/vmThread.cpp:466VMThread::run at share/vm/runtime/vmThread.cpp:273java_start at os/linux/vm/os_linux.cpp:852start_thread from /lib/libpthread.so.0clone 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
16
ここから並列 GC の話
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 Scavangeoop 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 rootsPSParallelCompact::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...