• Share
  • Email
  • Embed
  • Like
  • Save
  • Private Content
JVM-Reading-ParalleGC
 

JVM-Reading-ParalleGC

on

  • 823 views

 

Statistics

Views

Total Views
823
Views on SlideShare
822
Embed Views
1

Actions

Likes
3
Downloads
11
Comments
0

1 Embed 1

https://twitter.com 1

Accessibility

Upload Details

Uploaded via as Microsoft PowerPoint

Usage Rights

© All Rights Reserved

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

    JVM-Reading-ParalleGC JVM-Reading-ParalleGC Presentation Transcript

    • 並列 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 ...