Up Top

Memory allocation (& GC 処理) : メモリの確保処理 (GC 処理) : slow-path の処理 (4) GC 処理 : ParallelScavengeHeap の場合 : Minor GC の処理


概要(Summary)

ParallelScavengeHeap の Minor GC 処理は, PSScavenge::invoke() を呼び出すことで行われる (See: here for details).

実際の Minor GC 処理は, PSScavenge::invoke() から呼び出される PSScavenge::invoke_no_policy() の中に実装されている. この PSScavenge::invoke_no_policy() の処理は, 大きく分けると3つのフェーズからなる.

  1. strong root から辿れるオブジェクトを全てコピーする.

    この処理は OldToYoungRootsTask クラス, SerialOldToYoungRootsTask クラス, ScavengeRootsTask クラス, 及び ThreadRootsTask クラスに実装されている.

  2. コピーしたオブジェクトから再帰的に辿れる範囲を全てコピーする.

    この処理は StealTask クラスに実装されている.

  3. 参照オブジェクト(java.lang.ref オブジェクト)の処理を行う

    (See: here for details)

備考(Notes)

処理の流れ (概要)(Execution Flows : Summary)

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

処理の流れ (詳細)(Execution Flows : Details)

PSScavenge::invoke()

See: here for details

PSScavenge::invoke_no_policy()

See: here for details

PSScavenge::should_attempt_scavenge()

See: here for details

MutableSpace::accumulate_statistics()

See: here for details

MutableNUMASpace::accumulate_statistics()

(#Under Construction) See: here for details

ParallelScavengeHeap::accumulate_statistics_all_tlabs()

See: here for details

CollectedHeap::accumulate_statistics_all_tlabs()

(See: here for details)

Threads::create_thread_roots_tasks()

See: here for details

GCTaskManager::execute_and_wait()

See: here for details

GCTaskManager::add_list()

See: here for details

OldToYoungRootsTask::do_it()

See: here for details

CardTableExtension::scavenge_contents_parallel()

See: here for details

oopDesc::push_contents()

See: here for details

*Klass::oop_push_contents()

(#Under Construction)

PSPromotionManager::claim_or_forward_depth()

See: here for details

PSPromotionManager::claim_or_forward_internal_depth()

See: here for details

CardTableExtension::inline_write_ref_field_gc()

See: here for details

oopDesc::encode_store_heap_oop_not_null()

See: here for details

備考(Notes)

なお, narrowOop(See: here for details) の場合は, encode_heap_oop_not_null() で戻した後で書き込む.

See: here for details

PSPromotionManager::push_depth()

See: here for details

PSPromotionManager::drain_stacks()

See: here for details

PSPromotionManager::drain_stacks_depth()

See: here for details

PSPromotionManager::process_popped_location_depth()

See: here for details

PSPromotionManager::process_array_chunk()

(#Under Construction)

PSScavenge::copy_and_push_safe_barrier()

See: here for details

ParallelScavengeHeap::is_in_reserved()

See: here for details

PSPromotionManager::copy_to_survivor_space()

See: here for details

PSYoungPromotionLAB::allocate()

(#Under Construction)

PSOldPromotionLAB::allocate()

(#Under Construction)

PSPromotionManager::oop_promotion_failed()

See: here for details

PSScavenge::oop_promotion_failed()

See: here for details

SerialOldToYoungRootsTask::do_it()

See: here for details

CardTableExtension::scavenge_contents()

See: here for details

ScavengeRootsTask::do_it()

See: here for details

PSScavengeRootsClosure::do_oop()

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); }

PSScavengeRootsClosure::do_oop_work()

See: here for details

PSScavenge::should_scavenge(T* p)

See: here for details

ThreadRootsTask::do_it()

See: here for details

StealTask::do_it()

See: here for details

PSPromotionManager::steal_depth()

See: here for details

PSRefProcTaskExecutor::execute(ProcessTask& task)

See: here for details

PSRefProcTaskProxy::do_it()

See: here for details

PSIsAliveClosure::do_object_b()

See: here for details

PSKeepAliveClosure::do_oop()

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); }

PSKeepAliveClosure::do_oop_work()

See: here for details

PSScavenge::should_scavenge(T* p, MutableSpace* to_space)

See: here for details

PSRefProcTaskExecutor::execute(EnqueueTask& task)

See: here for details

PSRefEnqueueTaskProxy::do_it()

See: here for details

PSScavenge::clean_up_failed_promotion()

(#Under Construction) See: here for details

PSPromotionFailedClosure::do_object()

See: here for details

oopDesc::init_mark()

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)); }

markOopDesc::prototype_for_object()

See: here for details

PSYoungGen::swap_spaces()

See: here for details

MutableNUMASpace::update()

(#Under Construction) See: here for details

MutableSpace::update()

    ((cite: hotspot/src/share/vm/gc_implementation/shared/mutableSpace.hpp))

(1) (何もしない)

      virtual void update() { }

ParallelScavengeHeap::resize_all_tlabs()

See: here for details

MemoryService::track_memory_usage()

See: here for details

ParallelScavengeHeap::update_counters()

See: here for details

PSYoungGen::update_counters()

See: here for details

PSOldGen::update_counters()

See: here for details

PSGCAdaptivePolicyCounters::update_counters()

See: here for details

TraceMemoryManagerStats::TraceMemoryManagerStats()

See: here for details

TraceMemoryManagerStats::initialize()

See: here for details

MemoryService::gc_begin()

See: here for details

GCMemoryManager::gc_begin()

See: here for details

TraceMemoryManagerStats::~TraceMemoryManagerStats()

See: here for details

MemoryService::gc_end()

See: here for details

GCMemoryManager::gc_end()

See: here for details


This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.