hotspot/src/share/vm/gc_implementation/g1/g1CollectedHeap.cpp
bool
G1CollectedHeap::do_collection_pause_at_safepoint(double target_pause_time_ms) {
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert_at_safepoint(true /* should_be_vm_thread */);
guarantee(!is_gc_active(), "collection is not reentrant");
{- -------------------------------------------
(1) もし GC_locker によって GC が禁止されていれば, ここでリターン.
---------------------------------------- -}
if (GC_locker::check_active_before_gc()) {
return false;
}
{- -------------------------------------------
(1) (DTrace のフック点) (JVMTI のフック点)
(See: SvcGCMarker)
---------------------------------------- -}
SvcGCMarker sgcm(SvcGCMarker::MINOR);
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
ResourceMark rm;
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
if (PrintHeapAtGC) {
Universe::print_heap_before_gc();
}
{- -------------------------------------------
(1) (verify)
---------------------------------------- -}
verify_region_sets_optional();
verify_dirty_young_regions();
{- -------------------------------------------
(1) (以下のブロック内で実際の GC 処理が行われる)
---------------------------------------- -}
{
{- -------------------------------------------
(1) G1CollectorPolicy::decide_on_conc_mark_initiation() を呼んで,
今回の Minor GC 後に Concurrent marking を行うかどうかを決める.
---------------------------------------- -}
// This call will decide whether this pause is an initial-mark
// pause. If it is, during_initial_mark_pause() will return true
// for the duration of this pause.
g1_policy()->decide_on_conc_mark_initiation();
{- -------------------------------------------
(1) (トレース出力用の処理) & (トレース出力)
(#TODO increment_total_full_collections() もトレース出力用だっけ?)
---------------------------------------- -}
char verbose_str[128];
sprintf(verbose_str, "GC pause ");
if (g1_policy()->in_young_gc_mode()) {
if (g1_policy()->full_young_gcs())
strcat(verbose_str, "(young)");
else
strcat(verbose_str, "(partial)");
}
if (g1_policy()->during_initial_mark_pause()) {
strcat(verbose_str, " (initial-mark)");
// We are about to start a marking cycle, so we increment the
// full collection counter.
increment_total_full_collections();
}
// if PrintGCDetails is on, we'll print long statistics information
// in the collector policy code, so let's not print this as the output
// is messy if we do.
gclog_or_tty->date_stamp(PrintGC && PrintGCDateStamps);
TraceCPUTime tcpu(PrintGCDetails, true, gclog_or_tty);
TraceTime t(verbose_str, PrintGC && !PrintGCDetails, true, gclog_or_tty);
TraceCollectorStats tcs(g1mm()->incremental_collection_counters());
{- -------------------------------------------
(1) (トレース出力用の処理) (DTrace のフック点) (JMM のフック点)
(See: TraceMemoryManagerStats)
---------------------------------------- -}
TraceMemoryManagerStats tms(false /* fullGC */, gc_cause());
{- -------------------------------------------
(1) G1CollectedHeap::append_secondary_free_list_if_not_empty_with_lock() を呼んで,
SecondaryFreeRegionList の中身を MasterFreeRegionList に移動しておく.
(なお, develop オプションである InlineWarmCalls がセットされている場合は何もしない)
---------------------------------------- -}
// If the secondary_free_list is not empty, append it to the
// free_list. No need to wait for the cleanup operation to finish;
// the region allocation code will check the secondary_free_list
// and wait if necessary. If the G1StressConcRegionFreeing flag is
// set, skip this step so that the region allocation code has to
// get entries from the secondary_free_list.
if (!G1StressConcRegionFreeing) {
append_secondary_free_list_if_not_empty_with_lock();
}
{- -------------------------------------------
(1) #TODO (_gc_time_stamp を ++ しているだけだが...)
---------------------------------------- -}
increment_gc_time_stamp();
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
if (g1_policy()->in_young_gc_mode()) {
assert(check_young_list_well_formed(),
"young list should be well formed");
}
{- -------------------------------------------
(1)
---------------------------------------- -}
{ // Call to jvmpi::post_class_unload_events must occur outside of active GC
IsGCActiveMark x;
{- -------------------------------------------
(1) G1CollectedHeap::gc_prologue() を呼んで, GC のための前準備を行っておく.
---------------------------------------- -}
gc_prologue(false);
{- -------------------------------------------
(1) GC を行うことになったので, CollectedHeap::total_collections() の値を増加させておく.
(See: CollectedHeap::total_collections())
---------------------------------------- -}
increment_total_collections(false /* full gc */);
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
#if G1_REM_SET_LOGGING
gclog_or_tty->print_cr("\nJust chose CS, heap:");
print();
#endif
{- -------------------------------------------
(1) (verify)
---------------------------------------- -}
if (VerifyBeforeGC && total_collections() >= VerifyGCStartAt) {
HandleMark hm; // Discard invalid handles created during verification
gclog_or_tty->print(" VerifyBeforeGC:");
prepare_for_verify();
Universe::verify(false);
}
{- -------------------------------------------
(1) #ifdef COMPILER2 の場合には, (これから GC を行うので) DerivedPointerTable の値をリセットしておく.
(See: DerivedPointerTable)
---------------------------------------- -}
COMPILER2_PRESENT(DerivedPointerTable::clear());
{- -------------------------------------------
(1) ReferenceProcessor が動いていた場合は, 一時的に停止しておく.
(G1GC での参照オブジェクトの扱い方については
G1CollectedHeap::ref_processing_init() 中のコメントも参照, とのこと)
---------------------------------------- -}
// Please see comment in G1CollectedHeap::ref_processing_init()
// to see how reference processing currently works in G1.
//
// We want to turn off ref discovery, if necessary, and turn it back on
// on again later if we do. XXX Dubious: why is discovery disabled?
bool was_enabled = ref_processor()->discovery_enabled();
if (was_enabled) ref_processor()->disable_discovery();
{- -------------------------------------------
(1) G1CollectedHeap::release_mutator_alloc_region() を呼んで,
確保に使用している HeapRegion (G1AllocRegion) を
GC に備えて retire させておく.
---------------------------------------- -}
// Forget the current alloc region (we might even choose it to be part
// of the collection set!).
release_mutator_alloc_region();
{- -------------------------------------------
(1) (変数宣言など)
(GC 開始時の時刻およびその際のメモリ使用量. すぐ後ろで g1_policy 内にこの値を記録するために使用する)
---------------------------------------- -}
// The elapsed time induced by the start time below deliberately elides
// the possible verification above.
double start_time_sec = os::elapsedTime();
size_t start_used_bytes = used();
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
#if YOUNG_LIST_VERBOSE
gclog_or_tty->print_cr("\nBefore recording pause start.\nYoung_list:");
_young_list->print();
g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty);
#endif // YOUNG_LIST_VERBOSE
{- -------------------------------------------
(1) g1_policy 内に GC 開始時の時刻およびその際のメモリ使用量を記録する. (<= これって何に使われるんだっけ? #TODO)
---------------------------------------- -}
g1_policy()->record_collection_pause_start(start_time_sec,
start_used_bytes);
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
#if YOUNG_LIST_VERBOSE
gclog_or_tty->print_cr("\nAfter recording pause start.\nYoung_list:");
_young_list->print();
#endif // YOUNG_LIST_VERBOSE
{- -------------------------------------------
(1) Concurrent Mark を開始する場合は,
ConcurrentMark::checkpointRootsInitialPre() を呼んで next TAMS をセットしておく.
---------------------------------------- -}
if (g1_policy()->during_initial_mark_pause()) {
concurrent_mark()->checkpointRootsInitialPre();
}
{- -------------------------------------------
(1) G1CollectedHeap::save_marks() を呼んで,
全ての HeapRegion の top 位置を記録しておく.
---------------------------------------- -}
save_marks();
{- -------------------------------------------
(1) concurrent marking が実行中の場合は,
ConcurrentMark::drainAllSATBBuffers() を呼んで
残っている SATB バッファの処理を完了させておく.
(SATB 方式なので, write barrier が退避した mark 開始時点での値の処理を全て終わらせないといけない)
(ついでに, この処理に掛かった時間は g1_policy()->record_satb_drain_time() で記録される模様)
---------------------------------------- -}
// We must do this before any possible evacuation that should propagate
// marks.
if (mark_in_progress()) {
double start_time_sec = os::elapsedTime();
_cm->drainAllSATBBuffers();
double finish_mark_ms = (os::elapsedTime() - start_time_sec) * 1000.0;
g1_policy()->record_satb_drain_time(finish_mark_ms);
}
{- -------------------------------------------
(1) ConcurrentMark::set_oops_do_bound() を呼び出して,
この時点での mark stack と region stack の top 位置を記録しておく.
(mark stack や region stack を処理する際には, この top 位置までしか見ないようにして最適化している.
具体的には mark stack に対する oops_do() 処理と region stack に対する invalidate_entries_into_cset() 処理で行う最適化.
ConcurrentMark::set_oops_do_bound() のコメント参照)
---------------------------------------- -}
// Record the number of elements currently on the mark stack, so we
// only iterate over these. (Since evacuation may add to the mark
// stack, doing more exposes race conditions.) If no mark is in
// progress, this will be zero.
_cm->set_oops_do_bound();
{- -------------------------------------------
(1) #TODO (ConcurrentMark::newCSet() のコメントに書いてあるが grey なオブジェクトに関する最適化っぽい)
---------------------------------------- -}
if (mark_in_progress()) {
concurrent_mark()->newCSet();
}
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
#if YOUNG_LIST_VERBOSE
gclog_or_tty->print_cr("\nBefore choosing collection set.\nYoung_list:");
_young_list->print();
g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty);
#endif // YOUNG_LIST_VERBOSE
{- -------------------------------------------
(1) G1CollectorPolicy_BestRegionsFirst::choose_collection_set() を呼んで
GC 処理の対象とする region を選択する.
(指定された目標停止時間(以下の target_pause_time_ms) に基づいて決定.
選んだ region は G1CollectorPolicy::_collection_set に格納.)
---------------------------------------- -}
g1_policy()->choose_collection_set(target_pause_time_ms);
{- -------------------------------------------
(1) #TODO
(GC 対象に選んだ region ...)
---------------------------------------- -}
// We have chosen the complete collection set. If marking is
// active then, we clear the region fields of any of the
// concurrent marking tasks whose region fields point into
// the collection set as these values will become stale. This
// will cause the owning marking threads to claim a new region
// when marking restarts.
if (mark_in_progress()) {
concurrent_mark()->reset_active_task_region_fields_in_cset();
}
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
// Nothing to do if we were unable to choose a collection set.
#if G1_REM_SET_LOGGING
gclog_or_tty->print_cr("\nAfter pause, heap:");
print();
#endif
{- -------------------------------------------
(1) GC 対象に選んだ全ての HeapRegion に PrepareForRSScanningClosure を適用し,
HeapRegionRemSet オブジェクトの _iter_claimed フィールドを 0 にリセットしておく.
---------------------------------------- -}
PrepareForRSScanningClosure prepare_for_rs_scan;
collection_set_iterate(&prepare_for_rs_scan);
{- -------------------------------------------
(1) G1CollectedHeap::setup_surviving_young_words() を呼び出して, ... #TODO
(g1_policy()->young_cset_length() 分の大きさの配列を NEW_C_HEAP_ARRAY() で確保し,
_surviving_young_words フィールドにセットしている (ついでに配列の 0 クリアもしている)模様.
new 領域内の live オブジェクト量に比例するようだが, これ何に使うの??)
---------------------------------------- -}
setup_surviving_young_words();
{- -------------------------------------------
(1) G1CollectedHeap::get_gc_alloc_regions() を呼び出して, evacuation 先になる region を決定する.
(G1CollectedHeap::get_gc_alloc_regions() 内部では,
前回の GC 時に使用していた region (_retained_gc_alloc_regions フィールドに入っている) がまだ使えるなら引き続きそれを使用し,
ダメなら新しく一つ region を選ぶ模様.
See: G1CollectedHeap::get_gc_alloc_regions())
---------------------------------------- -}
// Set up the gc allocation regions.
get_gc_alloc_regions();
{- -------------------------------------------
(1) G1CollectedHeap::evacuate_collection_set() を呼んで,
選択した region に対して GC を実行する.
---------------------------------------- -}
// Actually do the work...
evacuate_collection_set();
{- -------------------------------------------
(1) 今回の GC で回収した HeapRegion をフリーリストにつなぐ.
---------------------------------------- -}
free_collection_set(g1_policy()->collection_set());
g1_policy()->clear_collection_set();
{- -------------------------------------------
(1) GC が終わったので, G1CollectedHeap::cleanup_surviving_young_words() を呼んで,
G1CollectedHeap::setup_surviving_young_words() で確保した配列を free しておく.
---------------------------------------- -}
cleanup_surviving_young_words();
{- -------------------------------------------
(1)
---------------------------------------- -}
// Start a new incremental collection set for the next pause.
g1_policy()->start_incremental_cset_building();
{- -------------------------------------------
(1)
---------------------------------------- -}
// Clear the _cset_fast_test bitmap in anticipation of adding
// regions to the incremental collection set for the next
// evacuation pause.
clear_cset_fast_test();
{- -------------------------------------------
(1) #TODO
---------------------------------------- -}
if (g1_policy()->in_young_gc_mode()) {
{- -------------------------------------------
(1)
---------------------------------------- -}
_young_list->reset_sampled_info();
{- -------------------------------------------
(1.1) (assert)
---------------------------------------- -}
// Don't check the whole heap at this point as the
// GC alloc regions from this pause have been tagged
// as survivors and moved on to the survivor list.
// Survivor regions will fail the !is_young() check.
assert(check_young_list_empty(false /* check_heap */),
"young list should be empty");
{- -------------------------------------------
(1.1) (トレース出力)
---------------------------------------- -}
#if YOUNG_LIST_VERBOSE
gclog_or_tty->print_cr("Before recording survivors.\nYoung List:");
_young_list->print();
#endif // YOUNG_LIST_VERBOSE
{- -------------------------------------------
(1.1) G1CollectorPolicy オブジェクト内に survivor 領域の情報を記録しておく.
(次回の G1CollectorPolicy::calculate_young_list_target_length() 時に使うため.
(See: G1CollectorPolicy::calculate_young_list_target_length()))
---------------------------------------- -}
g1_policy()->record_survivor_regions(_young_list->survivor_length(),
_young_list->first_survivor_region(),
_young_list->last_survivor_region());
{- -------------------------------------------
(1.1) survivor 領域の情報を incremental collection set 及び YoungList オブジェクト内で記録しておく(??)
---------------------------------------- -}
_young_list->reset_auxilary_lists();
}
{- -------------------------------------------
(1) _summary_bytes_used フィールドを更新しておく.
---------------------------------------- -}
if (evacuation_failed()) {
_summary_bytes_used = recalculate_used();
} else {
// The "used" of the the collection set have already been subtracted
// when they were freed. Add in the bytes evacuated.
_summary_bytes_used += g1_policy()->bytes_in_to_space();
}
{- -------------------------------------------
(1) 必要があれば Concurrent marking 処理を開始させる.
---------------------------------------- -}
if (g1_policy()->in_young_gc_mode() &&
g1_policy()->during_initial_mark_pause()) {
concurrent_mark()->checkpointRootsInitialPost();
set_marking_started();
// CAUTION: after the doConcurrentMark() call below,
// the concurrent marking thread(s) could be running
// concurrently with us. Make sure that anything after
// this point does not assume that we are the only GC thread
// running. Note: of course, the actual marking work will
// not start until the safepoint itself is released in
// ConcurrentGCThread::safepoint_desynchronize().
doConcurrentMark();
}
{- -------------------------------------------
(1) (デバッグ用の処理) (See: G1CollectedHeap::allocate_dummy_regions())
---------------------------------------- -}
allocate_dummy_regions();
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
#if YOUNG_LIST_VERBOSE
gclog_or_tty->print_cr("\nEnd of the pause.\nYoung_list:");
_young_list->print();
g1_policy()->print_collection_set(g1_policy()->inc_cset_head(), gclog_or_tty);
#endif // YOUNG_LIST_VERBOSE
{- -------------------------------------------
(1) G1CollectedHeap::init_mutator_alloc_region() を呼んで,
確保に使用している HeapRegion (G1AllocRegion) をセットしておく.
---------------------------------------- -}
init_mutator_alloc_region();
{- -------------------------------------------
(1) 処理にかかった時間を g1_policy 内に記録
---------------------------------------- -}
double end_time_sec = os::elapsedTime();
double pause_time_ms = (end_time_sec - start_time_sec) * MILLIUNITS;
g1_policy()->record_pause_time_ms(pause_time_ms);
g1_policy()->record_collection_pause_end();
{- -------------------------------------------
(1) (プロファイル情報の記録)(JMM 用) (See: MemoryUsage)
及び (JMM のフック点) でもある (See: LowMemoryDetector)
---------------------------------------- -}
MemoryService::track_memory_usage();
{- -------------------------------------------
(1) (verify)
---------------------------------------- -}
if (VerifyAfterGC && total_collections() >= VerifyGCStartAt) {
HandleMark hm; // Discard invalid handles created during verification
gclog_or_tty->print(" VerifyAfterGC:");
prepare_for_verify();
Universe::verify(false);
}
{- -------------------------------------------
(1) GC 前に ReferenceProcessor が動いていた場合は, 再開させておく.
---------------------------------------- -}
if (was_enabled) ref_processor()->enable_discovery();
{- -------------------------------------------
(1) 必要があれば, G1CollectedHeap::expand() を呼んでヒープ長を拡張しておく.
---------------------------------------- -}
{
size_t expand_bytes = g1_policy()->expansion_amount();
if (expand_bytes > 0) {
size_t bytes_before = capacity();
if (!expand(expand_bytes)) {
// We failed to expand the heap so let's verify that
// committed/uncommitted amount match the backing store
assert(capacity() == _g1_storage.committed_size(), "committed size mismatch");
assert(max_capacity() == _g1_storage.reserved_size(), "reserved size mismatch");
}
}
}
{- -------------------------------------------
(1)
---------------------------------------- -}
if (mark_in_progress()) {
concurrent_mark()->update_g1_committed();
}
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
#ifdef TRACESPINNING
ParallelTaskTerminator::print_termination_counts();
#endif
{- -------------------------------------------
(1) (assert) (See: G1CollectedHeap::gc_epilogue())
---------------------------------------- -}
gc_epilogue(false);
}
{- -------------------------------------------
(1) (デバッグ用の処理)
(ExitAfterGCNum オプションの処理.
ExitAfterGCNum が指定されている場合, total_collections() が ExitAfterGCNum 回数まで達していれば, ここで HotSpot の実行を終了する)
---------------------------------------- -}
if (ExitAfterGCNum > 0 && total_collections() == ExitAfterGCNum) {
gclog_or_tty->print_cr("Stopping after GC #%d", ExitAfterGCNum);
print_tracing_info();
vm_exit(-1);
}
}
{- -------------------------------------------
(1) (verify)
---------------------------------------- -}
verify_region_sets_optional();
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
TASKQUEUE_STATS_ONLY(if (ParallelGCVerbose) print_taskqueue_stats());
TASKQUEUE_STATS_ONLY(reset_taskqueue_stats());
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
if (PrintHeapAtGC) {
Universe::print_heap_after_gc();
}
{- -------------------------------------------
(1)
---------------------------------------- -}
g1mm()->update_counters();
{- -------------------------------------------
(1) (トレース出力)
---------------------------------------- -}
if (G1SummarizeRSetStats &&
(G1SummarizeRSetStatsPeriod > 0) &&
(total_collections() % G1SummarizeRSetStatsPeriod == 0)) {
g1_rem_set()->print_summary_info();
}
{- -------------------------------------------
(1) リターン
---------------------------------------- -}
return true;
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.