Jvm reading-parallel gc

1,972 views

Published on

1 Comment
3 Likes
Statistics
Notes
  • A nice tutorial on
    JVM Architecture Specification Basic The Heap Area Introduction, teach about the JVM Heap Area in details
    http://www.youtube.com/watch?v=c-A7ZzxjWUI

    JVM Architecture Specification Basic The Method Area explained, teach about the JVM method area
    http://www.youtube.com/watch?v=a5GzF2fSSCE
       Reply 
    Are you sure you want to  Yes  No
    Your message goes here
No Downloads
Views
Total views
1,972
On SlideShare
0
From Embeds
0
Number of Embeds
15
Actions
Shares
0
Downloads
23
Comments
1
Likes
3
Embeds 0
No embeds

No notes for slide

Jvm reading-parallel gc

  1. 1. 並列 GC 第 5 回 JVM ソースコードリーディングの会 (OpenJDK) 中村 実 [email_address] [email_address] Twitter @nminoru_jp
  2. 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. 3. OpenJDK6 の GC の種類 <ul><li>並列 GC は 2 種類ある </li></ul><ul><ul><li>従来の SerialGC の NewGC を並列化した UseParNewGC </li></ul></ul><ul><ul><li>新世代と旧世代の配置を動的に変更し、 NewGC の並列化を行った UseParallelGC </li></ul></ul><ul><ul><ul><li>UseParallelGC は -XX:+UseParallelOldGC をつけることで Full GC の並列化も可能 </li></ul></ul></ul><ul><ul><li>アルゴリズム的にはほとんど違いがない </li></ul></ul><ul><li>今日は UseParallelGC を読みます。 </li></ul>
  4. 4. 逐次 GC の簡単なおさらい <ul><li>世代別 GC </li></ul><ul><ul><li>マイナー GC(New GC) はコピー GC </li></ul></ul><ul><ul><li>メジャー GC(Full GC) はマークコンパクト GC </li></ul></ul>
  5. 5. 逐次 GC の簡単なおさらい <ul><li>空間配置 </li></ul><ul><ul><li>ヒープは New, Oldm Perm の 3 つに分かれる。 </li></ul></ul><ul><ul><li>New はさらに Eden, From, To に分かれる。 From と To は GC 時に切り替わる。 </li></ul></ul>Perm Old Gen. Eden From To
  6. 6. 逐次 GC の簡単なおさらい <ul><li>New GC </li></ul><ul><ul><li>Eden と From にあるオブジェクトのうち生きているものを To にコピーする。 </li></ul></ul><ul><ul><li>生きているオブジェクトとは </li></ul></ul><ul><ul><ul><li>(a) スタックや Old 世代から参照されていもの </li></ul></ul></ul><ul><ul><ul><li>(b) (a) からさらに参照を受けているもの。 </li></ul></ul></ul><ul><ul><li>GC 後は Eden と From が空になる。 From と To はラベルを交換する。 </li></ul></ul>Perm Old Gen. Eden From To
  7. 7. New GC の手順 <ul><li>Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/From への参照を持つオブジェクトを探す。 </li></ul><ul><ul><li>オブジェクトがまだコピーされていなければ、 To 領域にコピーし、移動先にポインタを張り替える </li></ul></ul><ul><ul><li>オブジェクトがすでにコピーされていれば移動先にポインタを張り替える </li></ul></ul><ul><li>To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 </li></ul>c b a
  8. 8. New GC の手順 <ul><li>Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/From への参照を持つオブジェクトを探す。 </li></ul><ul><ul><li>オブジェクトがまだコピーされていなければ、 To 領域にコピーし、移動先にポインタを張り替える </li></ul></ul><ul><ul><li>オブジェクトがすでにコピーされていれば移動先にポインタを張り替える </li></ul></ul><ul><li>To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 </li></ul>c b a b’
  9. 9. New GC の手順 <ul><li>Old を下位アドレスからオブジェクトを順にスキャンする、 Eden/From への参照を持つオブジェクトを探す。 </li></ul><ul><ul><li>オブジェクトがまだコピーされていなければ、 To 領域にコピーし、移動先にポインタを張り替える </li></ul></ul><ul><ul><li>オブジェクトがすでにコピーされていれば移動先にポインタを張り替える </li></ul></ul><ul><li>To 領域に移動したオブジェクトに対しても同様のスキャンを行う。 </li></ul>c b a b’
  10. 10. NewGC のポイント <ul><li>Forwarding pointer </li></ul><ul><ul><li>コピー元のオブジェクトの oopDesc の _mark を潰してコピー先のアドレスを記録する。 (src/share/vm/oops/mark.hpp) </li></ul></ul><ul><ul><li>すでに移動しているかどうかは LSB の 2 ビットを marked に変えて通知する。 </li></ul></ul>オブジェクトの本体 _klass 2 1 4 hash code mark biased lock age
  11. 11. ソースコードで確認 <ul><li>src/share/vm/memory/defNewGeneartion.cpp:524 </li></ul>void DefNewGeneration::collect() { // ルート (Old 領域を含む ) 内のオブジェクトをスキャンして Eden/From 空間 // を指すオブジェクトを探す gch->gen_process_strong_roots(_level, // To 空間にコピーさえたオブジェクトをスキャンする evacuate_followers.do_void();
  12. 12. ソースコードで確認 <ul><li>DefNewGeneration::copy_to_survivor_space at src/share/vm/memory/defNewGeneartion.cpp:720 </li></ul>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. 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. 14. 逐次 GC の簡単なおさらい <ul><li>Full GC </li></ul><ul><ul><li>ヒープ全体をマーキングして、生きているオブジェクトを Old に集める。 </li></ul></ul><ul><ul><li>生きているオブジェクトが Old に入りきらない場合には各空間でコンパクションする </li></ul></ul>Perm Old Gen. Eden From To
  15. 15. Full GC の手順 <ul><li>Phase1: marking </li></ul><ul><ul><li>スタックから参照されているオブジェクトをトレースしてマーキングする。 </li></ul></ul><ul><ul><li>vm/gc_imimplementation/shared/markSweep.inline.hpp </li></ul></ul><ul><li>Phase2: compute new address </li></ul><ul><ul><li>Old 空間内をコンパクションした場合の移動先を計算し、 forwarding pointer として書き込む </li></ul></ul><ul><li>Phase3: adjust pointers </li></ul><ul><ul><li>オブジェクトの参照 ( ポインタ ) を移動先のアドレスに書き換える </li></ul></ul><ul><li>Phase4: compaction(move) </li></ul><ul><ul><li>生きているオブジェクトだけを詰めて移動する。 </li></ul></ul><ul><li>GenMarkSweep::invoke_at_safepoint at src/share/vm/memory/genMarkSweep.cpp:59 </li></ul>
  16. 16. ここから並列 GC の話
  17. 17. UseParallelGC <ul><li>GC スレッドの並列化 </li></ul><ul><ul><li>GCTaskThread はシステムの CPU 数に合わせて生成される ( -XX:ParallelGCThreads= n で変更可能 ) </li></ul></ul><ul><ul><li>VMThread が GC を受けて、 GCTaskThread に処理を依頼する。 </li></ul></ul><ul><ul><ul><li>システムに一つのタスクキューに GCTask の派生型の処理を push すると、 GCTaskThread が pop して処理を開始する。 </li></ul></ul></ul><ul><ul><ul><li>parallelScavenge/gcTaskThread.cpp:95 </li></ul></ul></ul>
  18. 18. UseParallelGC <ul><li>VM_ParallelGCSystemGC::doit(hotspot/src/share/vm/gc_implementation/parallelScavenge/vmPSOperations.cpp) の中で GC を発生させる。 </li></ul>// 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. 19. Parallel Scavange <ul><li>New GC の並列版 </li></ul><ul><ul><li>New GC のほぼ全処理が並列化される </li></ul></ul><ul><ul><li>ランデブーポイントは 1 箇所 </li></ul></ul><ul><li>LAB </li></ul><ul><ul><li>オブジェクトを TO 領域にコピーする際に、 TLAB のようにあらかじめ一定範囲を GCTaskThread に割り当てる機構。アロケートのためのロックを減らせるのと、キャッシュの false sharing を防げる。 </li></ul></ul><ul><li>PSPromotionManager </li></ul><ul><ul><li>GC スレッド毎にインスタンスがあり、マーキングスタックや LAB を管理。 </li></ul></ul><ul><li>自分の仕事を先に済ませた GC スレッドは、他の GC スレッドの担当分を盗める (steal) ようになている。 </li></ul><ul><li>OldToYoungRootsTask </li></ul><ul><ul><li>Old 領域は複数の GC スレッドで処理するため 128 バイト単位でストライプ状に担当領域を分ける。 </li></ul></ul>
  20. 20. Parallel Scavange <ul><li>Parallel Scavange のスタートポイント PSScavenge::invoke_no_policy() のスタックトレース </li></ul>#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. 21. Parallel Scavange <ul><li>Old 世代のスキャン関数 CardTableExtension::scavenge_contents_parallel </li></ul>#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. 22. Parallel Scavange <ul><li>一番肝になる関数 PSPromotionManager::copy_to_survivor_space が呼び出されるまでのスタックトレース </li></ul>#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. 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. 24. Parallel Scavange <ul><li>タスクスチール </li></ul><ul><ul><li>マーキングスタックは share/vm/utilities/taskqueue.hpp のデータ構造でできている。これはロックフリーデータ構造 http ://www.nminoru.jp/~nminoru/programming/arora_dequeue.html </li></ul></ul><ul><ul><li>StealTask::StealTask </li></ul></ul>- TaskQueueSuper<N> - GenericTaskQueue<E, N> - OverflowTaskQueue<E, N> - OopStarTaskQueue
  25. 25. Parallel Compact <ul><li>Full GC の並列版 </li></ul><ul><ul><li>Mark & compact </li></ul></ul><ul><ul><li>Bitmap marking を行っている </li></ul></ul><ul><ul><li>移動先のデータを forwarding pointer を使わずに、 ParallelCompactData に記録する </li></ul></ul><ul><li>手順が少し異なる </li></ul><ul><ul><li>Phase 1: marking(parallel) </li></ul></ul><ul><ul><li>Phase 2: summary(serial) </li></ul></ul><ul><ul><ul><li>compute new address に相当 </li></ul></ul></ul><ul><ul><li>Phase 3: adjust roots(serial) </li></ul></ul><ul><ul><ul><li>スタック等の参照を変更する。ヒープ上のオブジェクトの pointer adjust は行わない。 </li></ul></ul></ul><ul><ul><li>Phase 4: compact perm(serial) </li></ul></ul><ul><ul><li>Phaes 5: compact(parallel) </li></ul></ul>
  26. 26. Parallel Compact <ul><li>Parallel Compact のスタートポイント PSScavenge::invoke_no_policy Full のスタックトレース </li></ul>#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. 27. Parallel Compact <ul><li>Phase1 marking で未マークなオブジェクトを見つけて PerMarkBitMap を打つところまでのスタックトレース </li></ul>#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. 28. Parallel Compact <ul><li>Phase1 marking で未マークなオブジェクトを見つけて PerMarkBitMap を打つところまでのスタックトレース </li></ul>#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. 29. Parallel Compact <ul><li>Phase3 adjust roots </li></ul><ul><li>PSParallelCompact::adjust_pointer のスタックトレース </li></ul>#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. 30. Parallel Compact <ul><li>PSParallelCompact::adjust_pointer at parallelScavenge/psParallelCompact.hpp:1318 </li></ul>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. 31. Parallel Compact <ul><li>Phase4 </li></ul>#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. 32. Parallel Compact <ul><li>Phase5 </li></ul>#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 ...

×