Successfully reported this slideshow.
We use your LinkedIn profile and activity data to personalize ads and to show you more relevant ads. You can change your ad preferences anytime.

Jvm reading-parallel gc

2,237 views

Published on

  • 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

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 ...

×