ParallelScavengeHeap の Minor GC 処理は, PSScavenge::invoke() を呼び出すことで行われる (See: here for details).
実際の Minor GC 処理は, PSScavenge::invoke() から呼び出される PSScavenge::invoke_no_policy() の中に実装されている. この PSScavenge::invoke_no_policy() の処理は, 大きく分けると3つのフェーズからなる.
strong root から辿れるオブジェクトを全てコピーする.
この処理は OldToYoungRootsTask クラス, SerialOldToYoungRootsTask クラス, ScavengeRootsTask クラス, 及び ThreadRootsTask クラスに実装されている.
コピーしたオブジェクトから再帰的に辿れる範囲を全てコピーする.
この処理は StealTask クラスに実装されている.
参照オブジェクト(java.lang.ref オブジェクト)の処理を行う
(See: here for details)
処理の並列化のために GCTaskThread (及び GCTaskManager, GCTask) クラスが使用されている (See: here for details).
PSScavenge::invoke() は, 基本的には PSScavenge::invoke_no_policy() を呼び出すだけだが, 必要な場合には Full GC まで実行することもある.
-> PSScavenge::invoke()
-> (1) PSScavenge::invoke_no_policy() を呼び出して Scavenge GC を行う.
-> PSScavenge::invoke_no_policy()
-> (1) PSScavenge::should_attempt_scavenge() 等で GC を実行していい状況かどうかをチェック. 実行不可能な場合はここでリターン.
-> PSScavenge::should_attempt_scavenge()
(1) 統計情報の更新処理
-> MutableSpace::accumulate_statistics() 等
(1) GCTaskQueue::create() で GCTaskQueue を作り, そこに GCTask をつめていく.
-> (1) 「Old 領域から Young 領域を指しているポインタの Scavenge 処理」を登録.
(より具体的に言うと OldToYoungRootsTask)
(1) 「Perm 領域から Young 領域を指しているポインタの Scavenge 処理」を登録.
(より具体的に言うと SerialOldToYoungRootsTask)
(1) 「Universe オブジェクト内から Young 領域を指しているポインタの Scavenge 処理」を登録.
(より具体的に言うと ScavengeRootsTask(ScavengeRootsTask::universe))
(1) 「JNI Handle から Young 領域を指しているポインタの Scavenge 処理」を登録.
(より具体的に言うと ScavengeRootsTask(ScavengeRootsTask::jni_handles))
(1) Threads::create_thread_roots_tasks() で「各 JavaThread の root の処理」を登録.
(より具体的に言うと ThreadRootsTask)
(1) その他, root となる箇所からの Scavenge 処理を登録.
ScavengeRootsTask(ScavengeRootsTask::object_synchronizer)
ScavengeRootsTask(ScavengeRootsTask::flat_profiler)
ScavengeRootsTask(ScavengeRootsTask::management)
ScavengeRootsTask(ScavengeRootsTask::system_dictionary)
ScavengeRootsTask(ScavengeRootsTask::jvmti)
ScavengeRootsTask(ScavengeRootsTask::code_cache)
(1) 最後に, ParallelGCThreads 個分だけ StealTask を登録.
(1) GCTaskManager::execute_and_wait() を呼び出し, 以上の GCTask を GCTaskThread 達に実行させる.
-> GCTaskManager::execute_and_wait()
-> GCTaskManager::add_list() で, GCTask を GCTaskThread 達に実行させる.
(なお, 実行させたスレッド自身は WaitForBarrierGCTask::wait_for() で GCTask が全て実行されるまで待機)
各 GCTask の処理の流れは以下のようになる.
* OldToYoungRootsTask の場合:
-> OldToYoungRootsTask::do_it()
(1) CardTableExtension::scavenge_contents_parallel() で, 対象範囲にあるポインタを収集する
-> CardTableExtension::scavenge_contents_parallel()
card table 中で dirty/younger_gen である箇所を調べ, 該当するアドレス範囲内の
オブジェクト中にあるポインタを PSPromotionManager に登録していく.
(なお, 処理対象の領域を ParallelGCThreads 個分に分けて並列処理する)
(具体的には, oopDesc::push_contents() を各オブジェクトに対して呼び出すことで
該当範囲のオブジェクト内のポインタを PSPromotionManager オブジェクトに登録していく.)
-> oopDesc::push_contents()
-> *Klass::oop_push_contents()
-> PSPromotionManager::claim_or_forward_depth()
-> PSPromotionManager::claim_or_forward_internal_depth()
* 処理対象のポインタが既に forward 済み(forwarding pointer)なら, 以下の処理を行う.
-> New 領域を指している場合には, CardTableExtension::inline_write_ref_field_gc() で
該当する card を youngergen_card (つまり「New 領域への参照有り」) に設定しておく.
-> oopDesc::encode_store_heap_oop_not_null() で, 処理対象のポインタ(p)をコピー先のアドレスに書き換える.
* そうでなければ, PSPromotionManager::push_depth() でキューにポインタを登録する.
-> PSPromotionManager::push_depth()
(2) PSPromotionManager::drain_stacks() で, 集めたポインタに対する再帰的なコピー処理を行う.
(なお引数の totally_drain は false として呼び出すので, ここで全て処理するわけではなく, ある程度処理したら途中で打ち切る.)
-> PSPromotionManager::drain_stacks()
-> PSPromotionManager::drain_stacks_depth()
-> PSPromotionManager::process_popped_location_depth()
* 処理対象のポインタが「途中まで処理済みの配列」である場合:
-> PSPromotionManager::process_array_chunk()
-> #TODO
* それ以外の場合:
-> PSScavenge::copy_and_push_safe_barrier()
(まだ未処理のオブジェクトであれば, PSPromotionManager::copy_to_survivor_space() でコピー処理を行ってフォワーディングポインタを埋め込む.)
(また, ParallelScavengeHeap::is_in_reserved() で世代をまたがるポインタかどうかをチェックし,
そうであれば CardTableExtension::inline_write_ref_field_gc() で
該当する card table の値を youngergen_card にしておく.)
-> PSPromotionManager::copy_to_survivor_space()
-> PSYoungPromotionLAB::allocate() または PSOldPromotionLAB::allocate() でコピー先を確保
-> Copy::aligned_disjoint_words() でコピー
-> oopDesc::cas_forward_to() で, コピー元にフォワーディングポインタ(コピー先のアドレス)を埋め込む.
-> oopDesc::push_contents() や PSPromotionManager::push_depth() で, オブジェクト内のポインタを PSPromotionManager 内に回収する.
(なお, 以上の処理で Old に promote しようとしたが容量不足で出来なかった場合, 代わりに以下の関数でポインタの回収処理等が行われる.)
-> PSPromotionManager::oop_promotion_failed()
-> oopDesc::push_contents()
-> PSScavenge::oop_promotion_failed()
* SerialOldToYoungRootsTask の場合:
-> SerialOldToYoungRootsTask::do_it()
(1) CardTableExtension::scavenge_contents() で, 対象範囲にあるポインタを収集する
-> CardTableExtension::scavenge_contents()
card table 中で dirty/younger_gen である箇所を調べ, 該当するアドレス範囲内の
オブジェクト中にあるポインタを PSPromotionManager に登録していく.
(具体的には, oopDesc::push_contents() を各オブジェクトに対して呼び出すことで
該当範囲のオブジェクト内のポインタを PSPromotionManager オブジェクトに登録していく.)
-> oopDesc::push_contents()
-> (同上)
(2) PSPromotionManager::drain_stacks() で, 集めたポインタに対する再帰的なコピー処理を行う.
(なお引数の totally_drain は false として呼び出すので, ここで全て処理するわけではなく, ある程度処理したら途中で打ち切る.)
-> PSPromotionManager::drain_stacks()
-> (同上)
* ScavengeRootsTask の場合:
-> ScavengeRootsTask::do_it()
(1) コンストラクタで指定された処理対象内のポインタを収集する.
(なお使用するクロージャーは PSScavengeRootsClosure)
* ScavengeRootsTask::universe の場合:
-> Universe::oops_do()
-> (See: here for details)
-> PSScavengeRootsClosure::do_oop()
-> PSScavengeRootsClosure::do_oop_work()
-> PSScavenge::copy_and_push_safe_barrier()
-> (同上)
-> ReferenceProcessor::oops_do()
-> (See: here for details)
* ScavengeRootsTask::jni_handles の場合:
-> JNIHandles::oops_do()
-> (See: here for details)
* ScavengeRootsTask::object_synchronizer の場合:
-> ObjectSynchronizer::oops_do()
-> (See: here for details)
* ScavengeRootsTask::flat_profiler の場合:
-> FlatProfiler::oops_do()
-> (See: here for details)
* ScavengeRootsTask::management の場合:
-> Management::oops_do()
-> (See: here for details)
* ScavengeRootsTask::system_dictionary の場合:
-> SystemDictionary::oops_do()
-> (See: here for details)
* ScavengeRootsTask::jvmti の場合:
-> JvmtiExport::oops_do()
-> (See: here for details)
* ScavengeRootsTask::code_cache の場合:
-> CodeCache::scavenge_root_nmethods_do()
-> (See: here for details)
(2) PSPromotionManager::drain_stacks() で, 集めたポインタに対する再帰的なコピー処理を行う.
(なお引数の totally_drain は false として呼び出すので, ここで全て処理するわけではなく, ある程度処理したら途中で打ち切る.)
-> PSPromotionManager::drain_stacks()
-> (同上)
* ThreadRootsTask の場合:
-> ThreadRootsTask::do_it()
(1) 処理対象のスレッド内にあるポインタを収集する.
(なお使用するクロージャーは PSScavengeRootsClosure と CodeBlobToOopClosure)
* 処理対象のスレッドが JavaThread であれば JavaThread::oops_do() を呼び出す.
* 処理対象のスレッドが VMThread であれば VMThread::oops_do() を呼び出す.
-> JavaThread::oops_do()
-> (See: here for details)
-> PSScavengeRootsClosure::do_oop()
-> (同上)
-> VMThread::oops_do()
-> (See: here for details)
-> PSScavengeRootsClosure::do_oop()
-> (同上)
(2) PSPromotionManager::drain_stacks() で, 集めたポインタに対する再帰的なコピー処理を行う.
(なお引数の totally_drain は false として呼び出すので, ここで全て処理するわけではなく, ある程度処理したら途中で打ち切る.)
-> PSPromotionManager::drain_stacks()
-> (同上)
* StealTask の場合:
-> StealTask::do_it()
(1) PSPromotionManager::drain_stacks() で, 自分のタスクキューが空になるまで処理を行う.
(なお引数の totally_drain は true として呼び出すので, ここで残っているもの全てを処理する)
(2) (自分の担当分は終わったので) PSPromotionManager::steal_depth() で他の GCTaskThread から仕事を奪い,
PSPromotionManager::process_popped_location_depth() 及び PSPromotionManager::drain_stacks_depth() で処理を行う.
以降, 未処理の仕事がなくなるまでこれを繰り返す.
(1) 参照オブジェクト(java.lang.ref オブジェクト)の処理を行う
-> ReferenceProcessor::setup_policy()
-> ReferenceProcessor::process_discovered_references()
(なお使用するクロージャーおよびAbstractRefProcTaskExecutorは,
PSIsAliveClosure, PSKeepAliveClosure, PSEvacuateFollowersClosure, PSRefProcTaskExecutor)
-> (See: here for details)
なお, 並列処理する場合はこの中で PSRefProcTaskExecutor が使用される.
PSRefProcTaskExecutor は, ReferenceProcessor::process_discovered_references() 内で
RefProcPhase1Task, RefProcPhase2Task, RefProcPhase3Task に対して以下のように呼び出される
-> PSRefProcTaskExecutor::execute()
-> PSRefProcTaskProxy::do_it()
(なお使用するクロージャーは,
PSIsAliveClosure, PSKeepAliveClosure, PSEvacuateFollowersClosure)
-> AbstractRefProcTaskExecutor::ProcessTask::work()
-> (実際には各サブクラスがオーバーライドしたメソッドが呼び出される
(See: RefProcPhase1Task::work(), RefProcPhase2Task::work(), RefProcPhase3Task::work()))
(See: here for details)
-> ReferenceProcessor::enqueue_discovered_references()
(なお使用するAbstractRefProcTaskExecutorは, PSRefProcTaskExecutor)
-> (See: here for details)
(1)
-> ... #TODO
(1) GC が失敗していれば, (この後 Full GC を行うことになるので) Full GC が行える状態にしておく
-> PSScavenge::clean_up_failed_promotion()
(1) GC が成功していれば, 後始末を行う.
-> PSYoungGen::swap_spaces()
-> ... (GC Ergonomics によるパラメータの最適化処理) #TODO
-> (See: here for details)
-> MutableNUMASpace::update() or MutableSpace::update() (ただし, MutableSpace::update() の方は何もしない空の関数)
-> ParallelScavengeHeap::resize_all_tlabs()
-> CollectedHeap::resize_all_tlabs()
-> (See: here for details)
->
(1)
(2) もし必要があれば (Minor GC が失敗した場合など) 以下の Full GC 処理のどちらかを行う.
-> PSParallelCompact::invoke_no_policy()
-> (See: here for details)
-> PSMarkSweep::invoke_no_policy()
-> (See: here for details)
See: here for details
See: here for details
See: here for details
See: here for details
(#Under Construction) See: here for details
See: here for details
(See: here for details)
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
(#Under Construction)
See: here for details
See: here for details
See: here for details
See: here for details
なお, narrowOop(See: here for details) の場合は, encode_heap_oop_not_null() で戻した後で書き込む.
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
(#Under Construction)
See: here for details
See: here for details
See: here for details
(#Under Construction)
(#Under Construction)
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
PSScavengeRootsClosure::do_oop_work() を呼び出すだけ.
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.inline.hpp))
void do_oop(oop* p) { PSScavengeRootsClosure::do_oop_work(p); }
void do_oop(narrowOop* p) { PSScavengeRootsClosure::do_oop_work(p); }
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
PSKeepAliveClosure::do_oop_work() を呼び出すだけ.
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psScavenge.cpp))
virtual void do_oop(oop* p) { PSKeepAliveClosure::do_oop_work(p); }
virtual void do_oop(narrowOop* p) { PSKeepAliveClosure::do_oop_work(p); }
See: here for details
See: here for details
See: here for details
See: here for details
(#Under Construction) See: here for details
See: here for details
mark フィールドの値を初期状態の値 (Klass::prototype_header() の値) にリセットする.
((cite: hotspot/src/share/vm/oops/oop.inline.hpp))
inline void oopDesc::init_mark() { set_mark(markOopDesc::prototype_for_object(this)); }
See: here for details
See: here for details
(#Under Construction) See: here for details
((cite: hotspot/src/share/vm/gc_implementation/shared/mutableSpace.hpp))
(1) (何もしない)
virtual void update() { }
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.