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.