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

PPTX
Jvm reading-synchronization
PDF
JVM-Reading-ConcurrentMarkSweep
PDF
C++ マルチスレッドプログラミング
PDF
はじめてのRuby拡張ライブラリ
PDF
新しい並列for構文のご提案
PPTX
Visual C++で使えるC++11
PDF
emc++ chapter32
PDF
組み込みでこそC++を使う10の理由
Jvm reading-synchronization
JVM-Reading-ConcurrentMarkSweep
C++ マルチスレッドプログラミング
はじめてのRuby拡張ライブラリ
新しい並列for構文のご提案
Visual C++で使えるC++11
emc++ chapter32
組み込みでこそC++を使う10の理由

What's hot (20)

PPTX
BoostAsioで可読性を求めるのは間違っているだろうか
KEY
関ジャバ JavaOne Tokyo 2012報告会
PDF
Async design with Unity3D
PDF
Effective Modern C++ 読書会 Item 35
PDF
Effective Modern C++ 勉強会#3 Item16
PDF
Effective modern-c++#9
PDF
Javaはどのように動くのか~スライドでわかるJVMの仕組み
PDF
Slub data structure
PDF
C++ マルチスレッド 入門
PPTX
async/await のしくみ
PDF
Effective Modern C++ 勉強会#3 Item 15
PDF
デバドラを書いてみよう!
PDF
Lisp Tutorial for Pythonista : Day 4
PDF
きつねさんでもわかるLlvm読書会 第2回
KEY
関東GPGPU勉強会 LLVM meets GPU
PPTX
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
PDF
講座Java入門
PPT
Google Perf Tools (tcmalloc) の使い方
PPTX
非同期処理の基礎
PDF
C++14 Overview
BoostAsioで可読性を求めるのは間違っているだろうか
関ジャバ JavaOne Tokyo 2012報告会
Async design with Unity3D
Effective Modern C++ 読書会 Item 35
Effective Modern C++ 勉強会#3 Item16
Effective modern-c++#9
Javaはどのように動くのか~スライドでわかるJVMの仕組み
Slub data structure
C++ マルチスレッド 入門
async/await のしくみ
Effective Modern C++ 勉強会#3 Item 15
デバドラを書いてみよう!
Lisp Tutorial for Pythonista : Day 4
きつねさんでもわかるLlvm読書会 第2回
関東GPGPU勉強会 LLVM meets GPU
JEP280: Java 9 で文字列結合の処理が変わるぞ!準備はいいか!? #jjug_ccc
講座Java入門
Google Perf Tools (tcmalloc) の使い方
非同期処理の基礎
C++14 Overview
Ad

Viewers also liked (20)

PPTX
UseNUMA做了什么?(2012-03-14)
PPTX
x86-64/Linuxに独自メモリ空間を勝手増設
PPTX
为啥别读HotSpot VM的源码(2012-03-03)
PPTX
Dentoo.LT12 並列処理・MPIの第一歩 20151025
PPTX
0円でできる自宅InfiniBandプログラム
PDF
SC16 NVIDIA NEWS
PPT
Jvm reading-parallel gc
PPTX
Nashorn on JDK 8 (ADC2013)
PPTX
Persistent-Memory-Programming-Model
PDF
プログラマ目線から見たRDMAのメリットと その応用例について
PDF
低遅延Ethernetとファブリックによるデータセンタ・ネットワーク
PDF
シーサーでのInfiniBand導入事例
PDF
MPIによる並列計算
PDF
Intrinsic Methods in HotSpot VM
PDF
ioDriceとInfiniBandとDRBDを利用したリアルタイムレプリケーション
PPTX
InfiniBand Presentation
PPTX
x86-64/Linuxに独自メモリ空間を勝手増設
PPTX
JVM @ Taobao - QCon Hangzhou 2011
PPTX
Implementing a JavaScript Engine
PDF
Concurrent Mark-Sweep Garbage Collection #jjug_ccc
UseNUMA做了什么?(2012-03-14)
x86-64/Linuxに独自メモリ空間を勝手増設
为啥别读HotSpot VM的源码(2012-03-03)
Dentoo.LT12 並列処理・MPIの第一歩 20151025
0円でできる自宅InfiniBandプログラム
SC16 NVIDIA NEWS
Jvm reading-parallel gc
Nashorn on JDK 8 (ADC2013)
Persistent-Memory-Programming-Model
プログラマ目線から見たRDMAのメリットと その応用例について
低遅延Ethernetとファブリックによるデータセンタ・ネットワーク
シーサーでのInfiniBand導入事例
MPIによる並列計算
Intrinsic Methods in HotSpot VM
ioDriceとInfiniBandとDRBDを利用したリアルタイムレプリケーション
InfiniBand Presentation
x86-64/Linuxに独自メモリ空間を勝手増設
JVM @ Taobao - QCon Hangzhou 2011
Implementing a JavaScript Engine
Concurrent Mark-Sweep Garbage Collection #jjug_ccc
Ad

Similar to JVM-Reading-ParalleGC (20)

PDF
GCをみればRTSが見えてくる、かも。。。
PDF
マーク&スイープ勉強会
PDF
小二病でもGCやりたい
PDF
GC in C++0x
PDF
GC黄金時代
PDF
Rのデータ構造とメモリ管理
PDF
JVMのGCアルゴリズムとチューニング
PDF
JVM的な何か@JVM Operation Casual Talk
PDF
Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6
PPT
2012年javaメモリリーク
PDF
Scalaで実装するGC
PPTX
C#/.NETがやっていること 第二版
PPTX
Jdk9で変更になる(かも知れない)jvmオプションの標準設定
PDF
GCを発生させないJVMとコーディングスタイル
PDF
CPUから見たG1GC
PDF
20190625 OpenACC 講習会 第3部
PDF
統計解析言語Rにおける大規模データ管理のためのboost.interprocessの活用
PDF
Java garbage-collection
PDF
できる!並列・並行プログラミング
ODP
コンカレントGc
GCをみればRTSが見えてくる、かも。。。
マーク&スイープ勉強会
小二病でもGCやりたい
GC in C++0x
GC黄金時代
Rのデータ構造とメモリ管理
JVMのGCアルゴリズムとチューニング
JVM的な何か@JVM Operation Casual Talk
Garbage First Garbage Collection (G1 GC) #jjug_ccc #ccc_cd6
2012年javaメモリリーク
Scalaで実装するGC
C#/.NETがやっていること 第二版
Jdk9で変更になる(かも知れない)jvmオプションの標準設定
GCを発生させないJVMとコーディングスタイル
CPUから見たG1GC
20190625 OpenACC 講習会 第3部
統計解析言語Rにおける大規模データ管理のためのboost.interprocessの活用
Java garbage-collection
できる!並列・並行プログラミング
コンカレントGc

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