UseParallelOldGC オプションが指定されている場合, ParallelScavengeHeap の Major GC 処理は, PSParallelCompact::invoke() を呼び出すことで行われる (See: here for details).
実際の Major GC 処理は, PSParallelCompact::invoke() から呼び出される PSParallelCompact::invoke_no_policy() の中に実装されている. この PSParallelCompact::invoke_no_policy() の処理は, 大きく分けると4つのフェーズからなる.
なお, ほとんどの処理は PSParallelCompact クラスで実装されている.
アルゴリズム自体は以下の論文で提案された手法(?).
Haim Kermany, Erez Petrank The Compressor: Concurrent, Incremental, and Parallel Compaction In Proceedings of the 2006 ACM SIGPLAN conference on Programming language design and implementation, pages 354-363
GC 中で使用される情報は, 以下のように記録/参照される.
PSParallelCompact::marking_phase() の段階で, marking 結果が ParMarkBitMap に保存される.
次の PSParallelCompact::summary_phase() の段階で, Region 単位で移動先のアドレスが決定され, ParallelCompactData::RegionData に格納される.
PSParallelCompact::adjust_roots() の段階では, ParMarkBitMap 及び ParallelCompactData::RegionData の情報を元に それぞれのオブジェクトの移動先アドレスを計算しながらポインタを修正していく.
最後の PSParallelCompact::compact_perm(), 及び PSParallelCompact::compact() の段階では, ...
処理は基本的にはマルチスレッドで行うが, Perm 領域に関してだけはシングルスレッドで行う. これは Perm 内の Klass オブジェクトについては「処理中のクラスオブジェクトから参照されている全てのオブジェクトは既に処理済みでないといけない」という制限があるため. この条件はシングルスレッドでスライディングコンパクションしていけば満たすことはできる.
処理は大きく分けると4フェーズ. このうち, 最初の 3つのフェーズは PSParallelCompact::invoke_no_policy() 内で実行される.
mark all the live objects. * summary phase (PSParallelCompact::summary_phase() で実行される)
calculate the destination of each object at the end of the collection. * compacting phase (PSParallelCompact::compact() で実行される)
move the objects to their destination. * clean up phase
update some references and reinitialize some variables.
GC 対象の領域は, 固定長の "region" という単位で分割して処理される. 各 region には, その情報を表すために ParallelCompactData オブジェクトに対応づけられる. region の境界上にもオブジェクトが存在する可能性はあるので, 一般的には region とオブジェクトの関係は下の図のようになる (region の先頭部分や終端部分では, その境界線をまたぐようにオブジェクトが存在する).
// region -----+---------------------+----------
// objects covered [ AAA )[ BBB )[ CCC )[ DDD )
この際に, 各 region 内の live object 量の計算も行われる (境界にまたがるようなオブジェクトについては, その region に含まれている分だけの大きさが加算される. 上の図のような状態なら, AAA と BBB と CCC と DDD が生きているとすると, AAA の一部と BBB と CCC と DDD の一部, の合計値になる).
marking phase でのマーク結果は, ParMarkBitMap というビットマップに格納される. このビットマップの書き換えや live object 量の更新処理は, 狂いが出ないようアトミックに行われる.
(この合計量の情報が, 各 region 内の live オブジェクトの コンパクション後の新しいアドレスを計算するために用いられる).
summary phase で計算する内容の具体例としては, 以下のようなものがある (詳細は ParallelCompactData 参照). * (前の region からはみ出して)その region の先頭部分にまでを占めている live オブジェクトの量 * その region 内での最初の live オブジェクトの位置 * この region 内のオブジェクトがコンパクション後にコピーされる先の region の数
summary phase では, 以上の内容に加えて "dense prefix" も計算する. "dense prefix" は, コンパクション後にも移動しない部分のこと (ヒープ領域の先頭部分の live オブジェクトは, コンパクション後にも同じ位置にとどまる. たとえ死んでいるオブジェクトがあった場合でも, コンパクション処理を高速化するために, ヒープ領域の先頭にはある程度ゴミが残っていてもよい, ということにして移動しない部分を作ることもある. 詳細は summarize_dense_prefix() 参照).
なお, summary phase の処理は, シングルスレッドで実行する.
compaction phase では, 実際にオブジェクトを新しいアドレスに移動させ, 併せてヒープや strong root 中の全てのポインタを新しいアドレスを指すように修正する.
なお, 現状の実装では, 複数の region にまたがるオブジェクトについては 移動はするがその中にあるポインタは更新されないようになっている. これは, そのオブジェクトのクラスへのポインタが更新されたかどうかが簡単には分からないため. これらのポインタについては, clean up phase の最後に改めて更新処理を行っている. 詳細は PSParallelCompact::update_deferred_objects() 参照.
compaction phase の処理は, region 単位で行われる. 処理が可能になった region は ready list に積まれ, GC スレッドが ready list から取り出してきてコンパクション処理を行う. なお, 「処理が可能になった region」とは, 具体的には以下のような region のことである. このような region は常に1つ以上存在する. また, ready list に対する要素の追加/削除はアトミックに行われる.
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp))
// The UseParallelOldGC collector is a stop-the-world garbage collector that
// does parts of the collection using parallel threads. The collection includes
// the tenured generation and the young generation. The permanent generation is
// collected at the same time as the other two generations but the permanent
// generation is collect by a single GC thread. The permanent generation is
// collected serially because of the requirement that during the processing of a
// klass AAA, any objects reference by AAA must already have been processed.
// This requirement is enforced by a left (lower address) to right (higher
// address) sliding compaction.
//
// There are four phases of the collection.
//
// - marking phase
// - summary phase
// - compacting phase
// - clean up phase
//
// Roughly speaking these phases correspond, respectively, to
// - mark all the live objects
// - calculate the destination of each object at the end of the collection
// - move the objects to their destination
// - update some references and reinitialize some variables
//
// These three phases are invoked in PSParallelCompact::invoke_no_policy(). The
// marking phase is implemented in PSParallelCompact::marking_phase() and does a
// complete marking of the heap. The summary phase is implemented in
// PSParallelCompact::summary_phase(). The move and update phase is implemented
// in PSParallelCompact::compact().
//
// A space that is being collected is divided into regions and with each region
// is associated an object of type ParallelCompactData. Each region is of a
// fixed size and typically will contain more than 1 object and may have parts
// of objects at the front and back of the region.
//
// region -----+---------------------+----------
// objects covered [ AAA )[ BBB )[ CCC )[ DDD )
//
// The marking phase does a complete marking of all live objects in the heap.
// The marking also compiles the size of the data for all live objects covered
// by the region. This size includes the part of any live object spanning onto
// the region (part of AAA if it is live) from the front, all live objects
// contained in the region (BBB and/or CCC if they are live), and the part of
// any live objects covered by the region that extends off the region (part of
// DDD if it is live). The marking phase uses multiple GC threads and marking
// is done in a bit array of type ParMarkBitMap. The marking of the bit map is
// done atomically as is the accumulation of the size of the live objects
// covered by a region.
//
// The summary phase calculates the total live data to the left of each region
// XXX. Based on that total and the bottom of the space, it can calculate the
// starting location of the live data in XXX. The summary phase calculates for
// each region XXX quantites such as
//
// - the amount of live data at the beginning of a region from an object
// entering the region.
// - the location of the first live data on the region
// - a count of the number of regions receiving live data from XXX.
//
// See ParallelCompactData for precise details. The summary phase also
// calculates the dense prefix for the compaction. The dense prefix is a
// portion at the beginning of the space that is not moved. The objects in the
// dense prefix do need to have their object references updated. See method
// summarize_dense_prefix().
//
// The summary phase is done using 1 GC thread.
//
// The compaction phase moves objects to their new location and updates all
// references in the object.
//
// A current exception is that objects that cross a region boundary are moved
// but do not have their references updated. References are not updated because
// it cannot easily be determined if the klass pointer KKK for the object AAA
// has been updated. KKK likely resides in a region to the left of the region
// containing AAA. These AAA's have there references updated at the end in a
// clean up phase. See the method PSParallelCompact::update_deferred_objects().
// An alternate strategy is being investigated for this deferral of updating.
//
// Compaction is done on a region basis. A region that is ready to be filled is
// put on a ready list and GC threads take region off the list and fill them. A
// region is ready to be filled if it empty of live objects. Such a region may
// have been initially empty (only contained dead objects) or may have had all
// its live objects copied out already. A region that compacts into itself is
// also ready for filling. The ready list is initially filled with empty
// regions and regions compacting into themselves. There is always at least 1
// region that can be put on the ready list. The regions are atomically added
// and removed from the ready list.
-> PSParallelCompact::invoke() -> PSScavenge::invoke_no_policy() (<= ScavengeBeforeFullGC オプションが指定されていれば) -> (See: here for details) -> PSParallelCompact::invoke_no_policy() -> (1) GC のための前準備を行う -> PSParallelCompact::pre_compact() (1) 生きているオブジェクト全てに印(mark)を付ける -> PSParallelCompact::marking_phase() (1) GCTaskQueue::create() で GCTaskQueue を作り, そこに GCTask をつめていく. -> (1) 「各種 root となりうる箇所からの Mark 処理」を登録. (MarkFromRootsTask) (1) Threads::create_thread_roots_marking_tasks() で「各 JavaThread の root の処理」を登録. (ThreadRootsMarkingTask) -> Threads::create_thread_roots_marking_tasks() (1) 最後に, ParallelGCThreads 個分だけ StealMarkingTask を登録. (1) GCTaskManager::add_list() を呼び出し, 以上の GCTask を GCTaskThread 達に実行させる. (なお, 実行させたスレッド自身は WaitForBarrierGCTask::wait_for() で GCTask が全て実行されるまで待機) -> GCTaskManager::add_list() -> WaitForBarrierGCTask::wait_for() 各 GCTask の処理の流れは以下のようになる. * MarkFromRootsTask の場合: -> MarkFromRootsTask::do_it() (1) strong root 内にあるポインタの収集を行う. この処理は, コンストラクタで指定された定数値に応じて処理範囲が異なる. (なお使用するクロージャーは PSParallelCompact::MarkAndPushClosure) * MarkFromRootsTask::universe の場合: -> Universe::oops_do() -> (See: here for details) -> PSParallelCompact::MarkAndPushClosure::do_oop() -> PSParallelCompact::mark_and_push() -> PSParallelCompact::mark_obj() -> ParMarkBitMap::mark_obj() -> BitMap::par_set_bit() -> ParallelCompactData::add_obj() -> ParallelCompactData::RegionData::add_live_obj() #TODO -> ParCompactionManager::push() * MarkFromRootsTask::jni_handles の場合: -> JNIHandles::oops_do() -> (See: here for details) * MarkFromRootsTask::object_synchronizer の場合: -> ObjectSynchronizer::oops_do() -> (See: here for details) * MarkFromRootsTask::flat_profiler の場合: -> FlatProfiler::oops_do() -> (See: here for details) * MarkFromRootsTask::management の場合: -> Management::oops_do() -> (See: here for details) * MarkFromRootsTask::system_dictionary の場合: -> SystemDictionary::oops_do() -> (See: here for details) * MarkFromRootsTask::jvmti の場合: -> JvmtiExport::oops_do() -> (See: here for details) * MarkFromRootsTask::code_cache の場合: -> (何もしない) (2) ParCompactionManager::follow_marking_stacks() で, 集めたポインタに対する再帰的な marking 処理を行う. -> ParCompactionManager::follow_marking_stacks() (oopDesc::follow_contents() や objArrayKlass::oop_follow_contents() による探索で 新しく見つかったポインタが増えなくなるまで再帰的に繰り返す) -> oopDesc::follow_contents() -> *Klass::oop_follow_contents() で再帰的にポインタを辿ってマークを付けていく. (#TODO クラス毎に少しずつ違うけど, 基本的には PSParallelCompact::mark_and_push() で辿るだけ??) (なお, ポインタ配列の場合には, _marking_stack ではなく _objarray_stack へのプッシュも行われる) -> objArrayKlass::oop_follow_contents() -> objArrayKlass::objarray_follow_contents() -> objArrayKlass::oop_follow_contents() -> (同上) * ThreadRootsMarkingTask の場合: -> ThreadRootsMarkingTask::do_it() (1) 処理対象のスレッド内にあるポインタを収集する. (なお使用するクロージャーは PSParallelCompact::MarkAndPushClosure と CodeBlobToOopClosure) * 処理対象のスレッドが JavaThread であれば JavaThread::oops_do() を呼び出す. * 処理対象のスレッドが VMThread であれば VMThread::oops_do() を呼び出す. -> JavaThread::oops_do() -> (See: here for details) -> PSParallelCompact::MarkAndPushClosure::do_oop() -> (同上) -> VMThread::oops_do() -> (See: here for details) -> PSParallelCompact::MarkAndPushClosure::do_oop() -> (同上) (2) ParCompactionManager::follow_marking_stacks() で, 集めたポインタに対する再帰的な marking 処理を行う. -> ParCompactionManager::follow_marking_stacks() -> (同上) * StealMarkingTask の場合: -> StealMarkingTask::do_it() ParCompactionManager::steal_objarray() や ParCompactionManager::steal() で 他の GCTaskThread から仕事を奪い, objArrayKlass::oop_follow_contents() や oopDesc::follow_contents(), 及び PSPromotionManager::drain_stacks_depth() で marking 処理を行う. 以降, 未処理の仕事がなくなるまでこれを繰り返す. (1) 以上の処理で見つかった参照オブジェクト(java.lang.ref オブジェクト)の処理を行う -> ReferenceProcessor::process_discovered_references() (なお使用するクロージャーおよびAbstractRefProcTaskExecutorは, PSParallelCompact::IsAliveClosure, PSParallelCompact::MarkAndPushClosure, PSParallelCompact::FollowStackClosure, RefProcTaskExecutor) -> (See: here for details) なお, 並列処理する場合はこの中で RefProcTaskExecutor が使用される. RefProcTaskExecutor は, ReferenceProcessor::process_discovered_references() 内で RefProcPhase1Task, RefProcPhase2Task, RefProcPhase3Task に対して以下のように呼び出される -> RefProcTaskExecutor::execute() -> RefProcTaskProxy::do_it() (なお使用するクロージャーは, PSParallelCompact::IsAliveClosure, PSParallelCompact::MarkAndPushClosure, PSParallelCompact::FollowStackClosure) -> AbstractRefProcTaskExecutor::ProcessTask::work() -> (実際には各サブクラスがオーバーライドしたメソッドが呼び出される (See: RefProcPhase1Task::work(), RefProcPhase2Task::work(), RefProcPhase3Task::work())) (See: here for details) (1) #TODO -> SystemDictionary::do_unloading() -> CodeCache::do_unloading() -> -> PSParallelCompact::follow_weak_klass_links() -> PSParallelCompact::follow_mdo_weak_refs() -> StringTable::unlink() -> SymbolTable::unlink() (1) 各 live object に対して, コンパクション後の新しいアドレスを計算する. -> PSParallelCompact::summary_phase() -> PSParallelCompact::summarize_spaces_quick() -> ParallelCompactData::summarize() -> PSParallelCompact::summarize_space() (Perm 領域用) -> PSParallelCompact::compute_dense_prefix() -> PSParallelCompact::first_dead_space_region() -> PSParallelCompact::dead_wood_limiter() -> PSParallelCompact::dead_wood_limit_region() -> PSParallelCompact::reclaimed_ratio() -> PSParallelCompact::fill_dense_prefix_end() -> ParallelCompactData::summarize_dense_prefix() -> ParallelCompactData::summarize() -> PSParallelCompact::summarize_space() (Old 領域用) -> (同上) -> ParallelCompactData::summarize() (Eden 用) -> ParallelCompactData::summarize() (From 用) -> ParallelCompactData::summarize() (To 用) (1) strong root 内のポインタを新しいアドレスに修正する -> PSParallelCompact::adjust_roots() (なお使用するクロージャーは PSParallelCompact::AdjustPointerClosure) -> Universe::oops_do() -> (See: here for details) -> PSParallelCompact::AdjustPointerClosure::do_oop() -> PSParallelCompact::adjust_pointer() -> ParallelCompactData::calc_new_pointer() -> oopDesc::encode_store_heap_oop_not_null() -> ReferenceProcessor::oops_do() -> (See: here for details) -> JNIHandles::oops_do() -> (See: here for details) -> Threads::oops_do() -> (See: here for details) -> ObjectSynchronizer::oops_do() -> (See: here for details) -> FlatProfiler::oops_do() -> (See: here for details) -> Management::oops_do() -> (See: here for details) -> JvmtiExport::oops_do() -> (See: here for details) -> SystemDictionary::oops_do() -> (See: here for details) -> JNIHandles::weak_oops_do() -> (See: here for details) -> CodeCache::oops_do() -> (See: here for details) -> StringTable::oops_do() -> (See: here for details) -> ReferenceProcessor::weak_oops_do() -> (See: here for details) (1) Perm 領域内の各 live object を新しいアドレスに移動させ, それらの中にあるポインタを新しいアドレスに修正する. -> PSParallelCompact::compact_perm() -> PSParallelCompact::move_and_update() -> PSParallelCompact::update_and_deadwood_in_dense_prefix() -> ParMarkBitMap::iterate() (なお使用するクロージャーは UpdateOnlyClosure と FillClosure) -> FillClosure::do_addr() -> UpdateOnlyClosure::do_addr() -> oopDesc::update_contents() -> Klass::oop_update_pointers() -> (#TODO クラス毎に少しずつ違うけど, 基本的には PSParallelCompact::adjust_pointer() で辿るだけ??) -> ParMarkBitMap::iterate() (なお使用するクロージャーは MoveAndUpdateClosure) -> MoveAndUpdateClosure::do_addr() -> ObjectStartArray::allocate_block() -> Copy::aligned_conjoint_words() -> oopDesc::update_contents() -> (同上) -> MoveAndUpdateClosure::update_state() (1) New/Old 領域内の各 live object を新しいアドレスに移動させ, それらの中にあるポインタを新しいアドレスに修正する. -> PSParallelCompact::compact() (1) GCTaskQueue::create() で GCTaskQueue を作り, そこに GCTask をつめていく. (1) 「dense prefix 以外の部分の live オブジェクトの移動処理およびポインタの修正処理」を登録. (DrainStacksCompactionTask) -> PSParallelCompact::enqueue_region_draining_tasks() (1) 「dense prefix 部分についての, live オブジェクト内のポインタの修正処理 および dead オブジェクトをダミーオブジェクトで上書きする処理」を登録. (UpdateDensePrefixTask) -> PSParallelCompact::enqueue_dense_prefix_tasks() (1) 最後に, ParallelGCThreads 個分だけ StealRegionCompactionTask を登録. -> PSParallelCompact::enqueue_region_stealing_tasks() (1) GCTaskManager::add_list() を呼び出し, 以上の GCTask を GCTaskThread 達に実行させる. (なお, 実行させたスレッド自身は WaitForBarrierGCTask::wait_for() で GCTask が全て実行されるまで待機) -> GCTaskManager::add_list() -> WaitForBarrierGCTask::wait_for() 各 GCTask の処理の流れは以下のようになる. * DrainStacksCompactionTask の場合: -> DrainStacksCompactionTask::do_it() -> ParCompactionManager::drain_region_stacks() -> PSParallelCompact::fill_and_update_region() -> PSParallelCompact::fill_region() -> ParMarkBitMap::iterate() (なお使用するクロージャーは MoveAndUpdateClosure) -> MoveAndUpdateClosure::do_addr() -> (同上) #TODO * UpdateDensePrefixTask の場合: -> UpdateDensePrefixTask::do_it() -> PSParallelCompact::update_and_deadwood_in_dense_prefix() -> (同上) * StealRegionCompactionTask の場合: -> StealRegionCompactionTask::do_it() -> #TODO (1) 複数 region にまたがっていたためにポインタの修正が行えていないオブジェクトに対して処理を行う. -> PSParallelCompact::update_deferred_objects() (1) GC 処理の後片付けを行う -> PSParallelCompact::post_compact() -> PSParallelCompact::clear_data_covering_space() -> SpaceInfo::publish_new_top() -> PSParallelCompact::absorb_live_data_from_eden() -> Universe::update_heap_info_at_gc() -> ModRefBarrierSet::clear() or ModRefBarrierSet::invalidate() -> Threads::gc_epilogue() -> CodeCache::gc_epilogue() -> JvmtiExport::gc_epilogue() (1) GC 結果に基づいて領域長を調整する -> (See: GC Ergonomics) -> ParallelScavengeHeap::resize_all_tlabs() -> PSPermGen::compute_new_size()
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
frame::gc_prologue() を呼び出すだけ.
((cite: hotspot/src/share/vm/runtime/thread.cpp))
static void frame_gc_prologue(frame* f, const RegisterMap* map) { f->gc_prologue(); }
See: here for details
(#Under Construction) See: here for details
See: here for details
See: here for details
PSParallelCompact::mark_and_push() を呼び出すだけ.
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp))
void PSParallelCompact::MarkAndPushClosure::do_oop(oop* p) { mark_and_push(_compaction_manager, p); }
void PSParallelCompact::MarkAndPushClosure::do_oop(narrowOop* p) { mark_and_push(_compaction_manager, p); }
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp))
(1) ParallelCompactData::add_obj(引数のオーバーロード版) を呼び出すだけ.
void add_obj(oop p, size_t len) { add_obj((HeapWord*)p, len); }
See: here for details
See: here for details
See: here for details
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp))
(1) _partial_obj_addr フィールドをセットするだけ
void set_partial_obj_addr(HeapWord* addr) { _partial_obj_addr = addr; }
引数のポインタを _marking_stack にプッシュするだけ.
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psCompactionManager.hpp))
// Save for later processing. Must not fail.
inline void push(oop obj) { _marking_stack.push(obj); }
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
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp))
(1) この SplitInfo オブジェクトの値が有効なものかどうかを返す.
(_src_region_idx フィールドの値が 0 より大きければ有効と見なす.
逆に 0 あれば無効と見なす.)
// Return true if this split info is valid (i.e., if a split has been
// recorded). The very first region cannot have a partial object and thus is
// never split, so 0 is the 'invalid' value.
bool is_valid() const { return _src_region_idx > 0; }
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
PSParallelCompact::adjust_pointer() を呼び出すだけ.
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.cpp))
void PSParallelCompact::AdjustPointerClosure::do_oop(oop* p) { adjust_pointer(p, _is_root); }
void PSParallelCompact::AdjustPointerClosure::do_oop(narrowOop* p) { adjust_pointer(p, _is_root); }
See: here for details
See: here for details
See: here for details
(#Under Construction)
(#Under Construction)
(#Under Construction)
(#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
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
See: here for details
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp))
(1) コンパクション先の region 数(destination count)が 0 なら true を返す.
bool available() const { return _dc_and_los < dc_one; }
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
(#Under Construction) See: here for details
See: here for details
(#Under Construction) See: here for details
(#Under Construction) See: here for details
(#Under Construction) See: here for details
See: here for details
_new_top フィールドの値を, 対応する Space オブジェクトの top にセットするだけ.
((cite: hotspot/src/share/vm/gc_implementation/parallelScavenge/psParallelCompact.hpp))
void publish_new_top() const { _space->set_top(_new_top); }
See: here for details
See: here for details
See: here for details
See: here for details
frame::gc_epilogue() を呼び出すだけ.
((cite: hotspot/src/share/vm/runtime/thread.cpp))
// GC support
static void frame_gc_epilogue(frame* f, const RegisterMap* map) { f->gc_epilogue(); }
See: here for details
See: here for details### pd_gc_epilog() (x86 の場合) See: here for details### pd_gc_epilog() (zero の場合) See: here for details
(#Under Construction) See: here for details
See: here for details
See: here for details
See: here for details
GrowableCache::gc_epilogue() を呼び出すだけ.
((cite: hotspot/src/share/vm/prims/jvmtiImpl.hpp))
void gc_epilogue() { _cache.gc_epilogue(); }
See: here for details
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.