hotspot/src/share/vm/runtime/safepoint.cpp
// Wake up all threads, so they are ready to resume execution after the safepoint
// operation has been carried out
void SafepointSynchronize::end() {
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(Threads_lock->owned_by_self(), "must hold Threads_lock");
assert((_safepoint_counter & 0x1) == 1, "must be odd");
{- -------------------------------------------
(1) _safepoint_counter カウンタをインクリメントしておく.
(これは JNI による高速フィールドアクセスのための処理.
See: [here](no305911u.html) for details)
---------------------------------------- -}
_safepoint_counter ++;
// memory fence isn't required here since an odd _safepoint_counter
// value can do no harm and a fence is issued below anyway.
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
DEBUG_ONLY(Thread* myThread = Thread::current();)
assert(myThread->is_VM_thread(), "Only VM thread can execute a safepoint");
{- -------------------------------------------
(1) (プロファイル情報の記録) & (プロファイル情報の出力)
(See: SafepointSynchronize::end_statistics())
---------------------------------------- -}
if (PrintSafepointStatistics) {
end_statistics(os::javaTimeNanos());
}
{- -------------------------------------------
(1) (デバッグ用の処理) (#ifdef ASSERT 時にのみ実行)
---------------------------------------- -}
#ifdef ASSERT
// A pending_exception cannot be installed during a safepoint. The threads
// may install an async exception after they come back from a safepoint into
// pending_exception after they unblock. But that should happen later.
for(JavaThread *cur = Threads::first(); cur; cur = cur->next()) {
assert (!(cur->has_pending_exception() &&
cur->safepoint_state()->is_at_poll_safepoint()),
"safepoint installed a pending exception");
}
#endif // ASSERT
{- -------------------------------------------
(1) Safepoint Polling page をアクセス不可にしていた場合は,
os::make_polling_page_readable() を呼んでアクセス可能な状態に戻しておく.
(なお, PageArmed は Safepoint Polling page をアクセス不可にした場合に 1 になる変数.
See: SafepointSynchronize::begin())
---------------------------------------- -}
if (PageArmed) {
// Make polling safepoint aware
os::make_polling_page_readable();
PageArmed = 0 ;
}
{- -------------------------------------------
(1) Interpreter::ignore_safepoints() (またはそれをサブクラスがオーバーライドしたもの) を呼んで,
インタープリタの dispatch table を通常時用のものに戻す.
(なお, SafepointSynchronize::begin() 内で呼ばれる Interpreter::notice_safepoints() と同様,
これは Template Interpreter の場合に必要となる処理.
C++ Interpreter の場合は特に何もする必要は無い)
---------------------------------------- -}
// Remove safepoint check from interpreter
Interpreter::ignore_safepoints();
{- -------------------------------------------
(1) 以下のブロック内で, 停止している JavaThread を起床させる.
なお, この処理は Safepoint_lock で排他した状態で行う.
---------------------------------------- -}
{
MutexLocker mu(Safepoint_lock);
{- -------------------------------------------
(1.1) (assert)
---------------------------------------- -}
assert(_state == _synchronized, "must be synchronized before ending safepoint synchronization");
{- -------------------------------------------
(1.1) SafepointSynchronize::_state を _not_synchronized に戻す.
順序も大事なので, OrderAccess::fence() でメモリバリアも張っている.
---------------------------------------- -}
// Set to not synchronized, so the threads will not go into the signal_thread_blocked method
// when they get restarted.
_state = _not_synchronized;
OrderAccess::fence();
{- -------------------------------------------
(1.1) (トレース出力)
---------------------------------------- -}
if (TraceSafepoint) {
tty->print_cr("Leaving safepoint region");
}
{- -------------------------------------------
(1.1) 全ての JavaThread を起床させる.
(起床処理は, ThreadSafepointState::restart() を呼び出すことで行う)
(なお, VMThreadHintNoPreempt オプションが指定されている場合には,
os::hint_no_preempt() を呼んで
VM Thread に time slice を多めに与えてくれるよう, OS に要望を出している.
コメントによると, これは, Solaris において
(この時点では VMThread が一番 CPU 使用時間が長いので)
起床処理の途中で VMThread がデスケジューリングされて
性能が悪くなっていたかららしい.
ただし, このコメントには追記があり,
現在はそうではないので, この処理には意味が無いとのこと.)
---------------------------------------- -}
// Start suspended threads
for(JavaThread *current = Threads::first(); current; current = current->next()) {
// A problem occurring on Solaris is when attempting to restart threads
// the first #cpus - 1 go well, but then the VMThread is preempted when we get
// to the next one (since it has been running the longest). We then have
// to wait for a cpu to become available before we can continue restarting
// threads.
// FIXME: This causes the performance of the VM to degrade when active and with
// large numbers of threads. Apparently this is due to the synchronous nature
// of suspending threads.
//
// TODO-FIXME: the comments above are vestigial and no longer apply.
// Furthermore, using solaris' schedctl in this particular context confers no benefit
if (VMThreadHintNoPreempt) {
os::hint_no_preempt();
}
ThreadSafepointState* cur_state = current->safepoint_state();
assert(cur_state->type() != ThreadSafepointState::_running, "Thread not suspended at safepoint");
cur_state->restart();
assert(cur_state->is_running(), "safepoint state has not been reset");
}
{- -------------------------------------------
(1.1) (DTrace のフック点) 及び
(プロファイル情報の記録)(JMM 用) ("sun.rt.safepointTime")
(See: RuntimeService::record_safepoint_end())
---------------------------------------- -}
RuntimeService::record_safepoint_end();
{- -------------------------------------------
(1.1) Safepoint 処理が終わったので, Threads_lock のロックを解除する.
(これは SafepointSynchronize::begin() 内で取得したロック.
See: SafepointSynchronize::begin())
---------------------------------------- -}
// Release threads lock, so threads can be created/destroyed again. It will also starts all threads
// blocked in signal_thread_blocked
Threads_lock->unlock();
}
{- -------------------------------------------
(1) #TODO
(CMS や G1GC 向けの処理.
concurrent thread を再開させる処理??)
---------------------------------------- -}
#ifndef SERIALGC
// If there are any concurrent GC threads resume them.
if (UseConcMarkSweepGC) {
ConcurrentMarkSweepThread::desynchronize(false);
} else if (UseG1GC) {
ConcurrentGCThread::safepoint_desynchronize();
}
#endif // SERIALGC
{- -------------------------------------------
(1) 現在時刻を _end_of_last_safepoint フィールドに記録しておく.
(この情報は, 以下の関数から参照されている.
See: SafepointSynchronize::last_non_safepoint_interval(), SafepointSynchronize::end_of_last_safepoint())
---------------------------------------- -}
// record this time so VMThread can keep track how much time has elasped
// since last safepoint.
_end_of_last_safepoint = os::javaTimeMillis();
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.