hotspot/src/share/vm/gc_implementation/g1/concurrentMarkThread.cpp
void ConcurrentMarkThread::run() {
{- -------------------------------------------
(1) ConcurrentGCThread::initialize_in_thread() を呼んで, 初期化を行う.
---------------------------------------- -}
initialize_in_thread();
{- -------------------------------------------
(1) フィールドの初期化
(処理の開始時間を記録しておく)
---------------------------------------- -}
_vtime_start = os::elapsedVTime();
{- -------------------------------------------
(1) ConcurrentGCThread::wait_for_universe_init() を呼んで,
HotSpot の初期化が終わるまで(= is_init_completed() が true になるまで)待機する.
---------------------------------------- -}
wait_for_universe_init();
{- -------------------------------------------
(1) (変数宣言など)
---------------------------------------- -}
G1CollectedHeap* g1h = G1CollectedHeap::heap();
G1CollectorPolicy* g1_policy = g1h->g1_policy();
G1MMUTracker *mmu_tracker = g1_policy->mmu_tracker();
Thread *current_thread = Thread::current();
{- -------------------------------------------
(1) (以下が ConcurrentMarkThread の処理のメインループ.
HotSpot が終了するまで(= _should_terminate フィールドが true になるまで) この while ループを実行.
See: ConcurrentMarkThread::stop())
---------------------------------------- -}
while (!_should_terminate) {
{- -------------------------------------------
(1.1) ConcurrentMarkThread::sleepBeforeNextCycle() を呼んで,
G1CollectedHeap::doConcurrentMark() で起こされるまで待つ.
(See: [here](no2935YzN.html) for details)
---------------------------------------- -}
// wait until started is set.
sleepBeforeNextCycle();
{
{- -------------------------------------------
(1.1) (変数宣言など)
---------------------------------------- -}
ResourceMark rm;
HandleMark hm;
double cycle_start = os::elapsedVTime();
double mark_start_sec = os::elapsedTime();
char verbose_str[128];
{- -------------------------------------------
(1.1) (トレース出力)
---------------------------------------- -}
if (PrintGC) {
gclog_or_tty->date_stamp(PrintGCDateStamps);
gclog_or_tty->stamp(PrintGCTimeStamps);
gclog_or_tty->print_cr("[GC concurrent-mark-start]");
}
{- -------------------------------------------
(1.1) 使用するアルゴリズムが Non-Generational な G1GC の場合
(= in_young_gc_mode() が false の場合
= G1Gen オプションが指定されていない場合) は,
以下の if ブロック内で Initial marking pause 処理を行う.
(なお, ... #TODO)
---------------------------------------- -}
if (!g1_policy->in_young_gc_mode()) {
{- -------------------------------------------
(1.1.1) 念のため, G1CollectedHeap::set_marking_complete() を呼び出しておく.
---------------------------------------- -}
// this ensures the flag is not set if we bail out of the marking
// cycle; normally the flag is cleared immediately after cleanup
g1h->set_marking_complete();
{- -------------------------------------------
(1.1.1) ユーザー指定の GC 時間制約を守る場合には
(= G1CollectorPolicy::adaptive_young_list_length() が true の場合には),
次の GC 実行までに必要な待ち時間を G1MMUTracker::when_ms() で計算し,
その時間分だけ os::sleep() で待機する.
---------------------------------------- -}
if (g1_policy->adaptive_young_list_length()) {
double now = os::elapsedTime();
double init_prediction_ms = g1_policy->predict_init_time_ms();
jlong sleep_time_ms = mmu_tracker->when_ms(now, init_prediction_ms);
os::sleep(current_thread, sleep_time_ms, false);
}
{- -------------------------------------------
(1.1.1) CMCheckpointRootsInitialClosure で Initial marking pause 処理を実行する.
---------------------------------------- -}
// We don't have to skip here if we've been asked to restart, because
// in the worst case we just enqueue a new VM operation to start a
// marking. Note that the init operation resets has_aborted()
CMCheckpointRootsInitialClosure init_cl(_cm);
strcpy(verbose_str, "GC initial-mark");
VM_CGC_Operation op(&init_cl, verbose_str);
VMThread::execute(&op);
}
{- -------------------------------------------
(1.1) (変数宣言など)
(iter はトレース出力用の変数)
---------------------------------------- -}
int iter = 0;
{- -------------------------------------------
(1.1) (以下の do...while ブロック内で concurrent marking 処理を行い,
最後に CMCheckpointRootsFinalClosure で Final marking pause 処理も行う.
なお, 処理が失敗した場合は ConcurrentMark::restart_for_overflow() が true に変わる
(See: ConcurrentMark::checkpointRootsFinal()).
この場合は成功するまで処理を繰り返す.
このために, ブロックの最後でこれをチェックして true だったらやり直し, という流れになっている.)
---------------------------------------- -}
do {
{- -------------------------------------------
(1.1.1) (トレース出力用の処理)
---------------------------------------- -}
iter++;
{- -------------------------------------------
(1.1.1) ConcurrentMark::markFromRoots() を呼んで,
root から辿れる範囲の Concurrent marking 処理を行う.
(ただし, 並行して Full GC が実行されてしまった場合は
(= ConcurrentMark::has_aborted() が true の場合は),
これ以上 marking 処理を続けても結果は不正なので, 何もしない)
---------------------------------------- -}
if (!cm()->has_aborted()) {
_cm->markFromRoots();
}
{- -------------------------------------------
(1.1.1) (トレース出力用の処理)
(_vtime_mark_accum は現在は以下のパスでのみ参照されている.
before_exit()
-> G1CollectedHeap::print_tracing_info()
-> ConcurrentMark::print_summary_info() (G1SummarizeConcMark オプション指定時のみ)
-> ConcurrentMarkThread::vtime_mark_accum())
---------------------------------------- -}
double mark_end_time = os::elapsedVTime();
double mark_end_sec = os::elapsedTime();
_vtime_mark_accum += (mark_end_time - cycle_start);
{- -------------------------------------------
(1.1.1) (以下の if ブロック内で Final marking pause を行う.
ただし, 並行して Full GC が実行されてしまった場合は
(= ConcurrentMark::has_aborted() が true の場合は),
これ以上 marking 処理を続けても結果は不正なので, 何もしない)
---------------------------------------- -}
if (!cm()->has_aborted()) {
{- -------------------------------------------
(1.1.1.1) ユーザー指定の GC 時間制約を守る場合には
(= G1CollectorPolicy::adaptive_young_list_length() が true の場合には),
次の GC 実行までに必要な待ち時間を G1MMUTracker::when_ms() で計算し,
その時間分だけ os::sleep() で待機する.
---------------------------------------- -}
if (g1_policy->adaptive_young_list_length()) {
double now = os::elapsedTime();
double remark_prediction_ms = g1_policy->predict_remark_time_ms();
jlong sleep_time_ms = mmu_tracker->when_ms(now, remark_prediction_ms);
os::sleep(current_thread, sleep_time_ms, false);
}
{- -------------------------------------------
(1.1.1.1) (トレース出力)
---------------------------------------- -}
if (PrintGC) {
gclog_or_tty->date_stamp(PrintGCDateStamps);
gclog_or_tty->stamp(PrintGCTimeStamps);
gclog_or_tty->print_cr("[GC concurrent-mark-end, %1.7lf sec]",
mark_end_sec - mark_start_sec);
}
{- -------------------------------------------
(1.1.1.1) CMCheckpointRootsFinalClosure で Final marking pause 処理を実行する.
---------------------------------------- -}
CMCheckpointRootsFinalClosure final_cl(_cm);
sprintf(verbose_str, "GC remark");
VM_CGC_Operation op(&final_cl, verbose_str);
VMThread::execute(&op);
}
{- -------------------------------------------
(1.1.1) (トレース出力)
---------------------------------------- -}
if (cm()->restart_for_overflow() &&
G1TraceMarkStackOverflow) {
gclog_or_tty->print_cr("Restarting conc marking because of MS overflow "
"in remark (restart #%d).", iter);
}
{- -------------------------------------------
(1.1.1) (トレース出力)
---------------------------------------- -}
if (cm()->restart_for_overflow()) {
if (PrintGC) {
gclog_or_tty->date_stamp(PrintGCDateStamps);
gclog_or_tty->stamp(PrintGCTimeStamps);
gclog_or_tty->print_cr("[GC concurrent-mark-restart-for-overflow]");
}
}
{- -------------------------------------------
(1.1) (ConcurrentMark::restart_for_overflow() が true になっていたらやり直し)
---------------------------------------- -}
} while (cm()->restart_for_overflow());
{- -------------------------------------------
(1.1) (変数宣言など)
(counting_start_time はトレース出力用の変数)
---------------------------------------- -}
double counting_start_time = os::elapsedVTime();
{- -------------------------------------------
(1.1) ?? #TODO
---------------------------------------- -}
// YSR: These look dubious (i.e. redundant) !!! FIX ME
slt()->manipulatePLL(SurrogateLockerThread::acquirePLL);
slt()->manipulatePLL(SurrogateLockerThread::releaseAndNotifyPLL);
{- -------------------------------------------
(1.1) ConcurrentMark::calcDesiredRegions() を呼んで,
生きているオブジェクトの量を計算しておく.
(ついでに, (トレース出力)も出している)
(ただし, 並行して Full GC が実行されてしまった場合は
(= ConcurrentMark::has_aborted() が true の場合は),
これ以上 marking 処理を続けても結果は不正なので, 何もしない)
---------------------------------------- -}
if (!cm()->has_aborted()) {
double count_start_sec = os::elapsedTime();
if (PrintGC) {
gclog_or_tty->date_stamp(PrintGCDateStamps);
gclog_or_tty->stamp(PrintGCTimeStamps);
gclog_or_tty->print_cr("[GC concurrent-count-start]");
}
_sts.join();
_cm->calcDesiredRegions();
_sts.leave();
if (!cm()->has_aborted()) {
double count_end_sec = os::elapsedTime();
if (PrintGC) {
gclog_or_tty->date_stamp(PrintGCDateStamps);
gclog_or_tty->stamp(PrintGCTimeStamps);
gclog_or_tty->print_cr("[GC concurrent-count-end, %1.7lf]",
count_end_sec - count_start_sec);
}
}
}
{- -------------------------------------------
(1.1) (トレース出力用の処理)
(_vtime_count_accum は現在は以下のパスでのみ参照されている.
before_exit()
-> G1CollectedHeap::print_tracing_info()
-> ConcurrentMark::print_summary_info() (G1SummarizeConcMark オプション指定時のみ)
-> ConcurrentMarkThread::vtime_count_accum())
---------------------------------------- -}
double end_time = os::elapsedVTime();
_vtime_count_accum += (end_time - counting_start_time);
{- -------------------------------------------
(1.1) (トレース出力用の処理)
(_vtime_accum は現在は以下のパスでのみ参照されている. ...)
---------------------------------------- -}
// Update the total virtual time before doing this, since it will try
// to measure it to get the vtime for this marking. We purposely
// neglect the presumably-short "completeCleanup" phase here.
_vtime_accum = (end_time - _vtime_start);
{- -------------------------------------------
(1.1) (以下の if ブロック内で Live Data Counting & Cleanup を行う.
ただし, 並行して Full GC が実行されてしまった場合は
(= ConcurrentMark::has_aborted() が true の場合は),
これ以上 marking 処理を続けても結果は不正なので, 処理は行わない.
代わりに, G1CollectedHeap::set_marking_complete() を呼んで
Concurrent Mark 処理が終了したことが分かるようにしておく.)
---------------------------------------- -}
if (!cm()->has_aborted()) {
{- -------------------------------------------
(1.1.1) ユーザー指定の GC 時間制約を守る場合には
(= G1CollectorPolicy::adaptive_young_list_length() が true の場合には),
次の GC 実行までに必要な待ち時間を G1MMUTracker::when_ms() で計算し,
その時間分だけ os::sleep() で待機する.
---------------------------------------- -}
if (g1_policy->adaptive_young_list_length()) {
double now = os::elapsedTime();
double cleanup_prediction_ms = g1_policy->predict_cleanup_time_ms();
jlong sleep_time_ms = mmu_tracker->when_ms(now, cleanup_prediction_ms);
os::sleep(current_thread, sleep_time_ms, false);
}
{- -------------------------------------------
(1.1.1) CMCleanUp で Live Data Counting & Cleanup 処理を実行する.
---------------------------------------- -}
CMCleanUp cl_cl(_cm);
sprintf(verbose_str, "GC cleanup");
VM_CGC_Operation op(&cl_cl, verbose_str);
VMThread::execute(&op);
{- -------------------------------------------
(1.1) (以下は, 並行して Full GC が実行された場合)
G1CollectedHeap::set_marking_complete() を呼んで
Concurrent Mark 処理が終了したことが分かるようにしておく.
---------------------------------------- -}
} else {
g1h->set_marking_complete();
}
{- -------------------------------------------
(1.1) Cleanup 処理で空の HeapRegion が見つかっていた場合は
(= G1CollectedHeap::free_regions_coming() が true の場合は),
以下の if ブロック内で処理を行う
(See: ConcurrentMark::cleanup())
---------------------------------------- -}
// Check if cleanup set the free_regions_coming flag. If it
// hasn't, we can just skip the next step.
if (g1h->free_regions_coming()) {
// The following will finish freeing up any regions that we
// found to be empty during cleanup. We'll do this part
// without joining the suspendible set. If an evacuation pause
// takes place, then we would carry on freeing regions in
// case they are needed by the pause. If a Full GC takes
// place, it would wait for us to process the regions
// reclaimed by cleanup.
{- -------------------------------------------
(1.1.1) (変数宣言など)
(cleanup_start_sec はトレース出力用の変数)
---------------------------------------- -}
double cleanup_start_sec = os::elapsedTime();
{- -------------------------------------------
(1.1.1) (トレース出力)
---------------------------------------- -}
if (PrintGC) {
gclog_or_tty->date_stamp(PrintGCDateStamps);
gclog_or_tty->stamp(PrintGCTimeStamps);
gclog_or_tty->print_cr("[GC concurrent-cleanup-start]");
}
{- -------------------------------------------
(1.1.1)
---------------------------------------- -}
// Now do the remainder of the cleanup operation.
_cm->completeCleanup();
{- -------------------------------------------
(1.1.1) free region 処理の完了を待っているスレッドがいるかもしれないので,
G1CollectedHeap::reset_free_regions_coming() を呼んで
処理の完了を通知しておく.
(See: G1CollectedHeap::wait_while_free_regions_coming())
(なおコメントによると,
この処理は sts に join() する前に行わないとデッドロックの危険があるので注意,
とのこと)
---------------------------------------- -}
// Notify anyone who's waiting that there are no more free
// regions coming. We have to do this before we join the STS,
// otherwise we might deadlock: a GC worker could be blocked
// waiting for the notification whereas this thread will be
// blocked for the pause to finish while it's trying to join
// the STS, which is conditional on the GC workers finishing.
g1h->reset_free_regions_coming();
{- -------------------------------------------
(1.1.1)
---------------------------------------- -}
_sts.join();
g1_policy->record_concurrent_mark_cleanup_completed();
_sts.leave();
{- -------------------------------------------
(1.1.1) (トレース出力)
---------------------------------------- -}
double cleanup_end_sec = os::elapsedTime();
if (PrintGC) {
gclog_or_tty->date_stamp(PrintGCDateStamps);
gclog_or_tty->stamp(PrintGCTimeStamps);
gclog_or_tty->print_cr("[GC concurrent-cleanup-end, %1.7lf]",
cleanup_end_sec - cleanup_start_sec);
}
}
{- -------------------------------------------
(1.1) (assert)
---------------------------------------- -}
guarantee(cm()->cleanup_list_is_empty(),
"at this point there should be no regions on the cleanup list");
{- -------------------------------------------
(1.1) (トレース出力)
---------------------------------------- -}
if (cm()->has_aborted()) {
if (PrintGC) {
gclog_or_tty->date_stamp(PrintGCDateStamps);
gclog_or_tty->stamp(PrintGCTimeStamps);
gclog_or_tty->print_cr("[GC concurrent-mark-abort]");
}
}
{- -------------------------------------------
(1.1) ConcurrentMark::clearNextBitmap() を呼んで, _nextMarkBitMap をクリアする.
(なお, この処理は適宜 SuspendibleThreadSet::yield() しながら行うので,
この処理の間は SuspendibleThreadSet オブジェクト(_sts)にカレントスレッドを登録しておく)
---------------------------------------- -}
// we now want to allow clearing of the marking bitmap to be
// suspended by a collection pause.
_sts.join();
_cm->clearNextBitmap();
_sts.leave();
{- -------------------------------------------
(1.1) (ここまでが ResourceMark や HandleMark のスコープ)
---------------------------------------- -}
}
{- -------------------------------------------
(1.1) (Full GC の完了を待っているスレッドがいるかもしれないので)
G1CollectedHeap::increment_full_collections_completed() を呼び出して
待っているスレッドがいれば起床させておく.
(See: VM_G1IncCollectionPause::doit_epilogue())
---------------------------------------- -}
// Update the number of full collections that have been
// completed. This will also notify the FullGCCount_lock in case a
// Java thread is waiting for a full GC to happen (e.g., it
// called System.gc() with +ExplicitGCInvokesConcurrent).
_sts.join();
g1h->increment_full_collections_completed(true /* concurrent */);
_sts.leave();
{- -------------------------------------------
(1) (ここまでが ConcurrentMarkThread の処理のメインループ. _should_terminate が true の間は以上の処理を繰り返す)
---------------------------------------- -}
}
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(_should_terminate, "just checking");
{- -------------------------------------------
(1) ConcurrentGCThread::terminate() を呼び出して,
ConcurrentMarkThread::stop() 内でこのスレッドの終了を待っているスレッドを起床させる.
(See: ConcurrentMarkThread::stop())
---------------------------------------- -}
terminate();
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.