hotspot/src/share/vm/memory/referenceProcessor.cpp
なお, ReferenceProcessor::discover_reference() での扱い方には 2種類のポリシーがあり得るとのこと.
ReferenceBasedDiscovery
処理対象の領域内(span内)に入っていない参照オブジェクトについては, (参照オブジェクトだけれども) そこからの参照を強参照と見なす.
(= 逆に言うと, 強参照以外の参照として扱われるのは, 参照オブジェクト自身とその差し先が同じ span に入っている場合だけ)
ReferentBasedDiscovery
参照オブジェクトから指されているものが処理対象の範囲内(span内)にあれば, それを指している参照オブジェクトは特殊な参照(強参照以外の参照)として扱う.
このポリシーのどちらを使用するかは, RefDiscoveryPolicy コマンドラインオプションで指定できる模様.
// We mention two of several possible choices here:
// #0: if the reference object is not in the "originating generation"
// (or part of the heap being collected, indicated by our "span"
// we don't treat it specially (i.e. we scan it as we would
// a normal oop, treating its references as strong references).
// This means that references can't be enqueued unless their
// referent is also in the same span. This is the simplest,
// most "local" and most conservative approach, albeit one
// that may cause weak references to be enqueued least promptly.
// We call this choice the "ReferenceBasedDiscovery" policy.
// #1: the reference object may be in any generation (span), but if
// the referent is in the generation (span) being currently collected
// then we can discover the reference object, provided
// the object has not already been discovered by
// a different concurrently running collector (as may be the
// case, for instance, if the reference object is in CMS and
// the referent in DefNewGeneration), and provided the processing
// of this reference object by the current collector will
// appear atomic to every other collector in the system.
// (Thus, for instance, a concurrent collector may not
// discover references in other generations even if the
// referent is in its own generation). This policy may,
// in certain cases, enqueue references somewhat sooner than
// might Policy #0 above, but at marginally increased cost
// and complexity in processing these references.
// We call this choice the "RefeferentBasedDiscovery" policy.
bool ReferenceProcessor::discover_reference(oop obj, ReferenceType rt) {
{- -------------------------------------------
(1) もし discovery 処理が停止されているか (= _discovering_refs フィールドが false になっているか),
または RegisterReferences オプションが指定されていない場合には,
特に処理は行わずにここでリターン.
(See: ReferenceProcessor::disable_discovery())
---------------------------------------- -}
// We enqueue references only if we are discovering refs
// (rather than processing discovered refs).
if (!_discovering_refs || !RegisterReferences) {
return false;
}
{- -------------------------------------------
(1) もし処理対象の参照オブジェクトの next フィールドが
null でなければ (= 参照オブジェクトが Active 状態でなければ)
特別扱いする必要は無いので, ここでリターン.
(See: java.lang.ref.Reference)
---------------------------------------- -}
// We only enqueue active references.
oop next = java_lang_ref_Reference::next(obj);
if (next != NULL) {
return false;
}
{- -------------------------------------------
(1) もしポリシーが ReferenceBasedDiscovery の場合,
参照オブジェクトが処理対象の領域内(span 内)になければ
ここでリターン.
---------------------------------------- -}
HeapWord* obj_addr = (HeapWord*)obj;
if (RefDiscoveryPolicy == ReferenceBasedDiscovery &&
!_span.contains(obj_addr)) {
// Reference is not in the originating generation;
// don't treat it specially (i.e. we want to scan it as a normal
// object with strong references).
return false;
}
{- -------------------------------------------
(1) もし, 処理対象の参照オブジェクトの差し先が
他の強参照から到達可能であれば
(つまり live であることが確定していれば)
何もする必要は無いので, ここでリターン.
(強参照から到達可能かどうかは,
引数で渡されたクロージャー(以下の is_alive_non_header)により
差し先のオブジェクトの生死を調べることで行っている.)
---------------------------------------- -}
// We only enqueue references whose referents are not (yet) strongly
// reachable.
if (is_alive_non_header() != NULL) {
verify_referent(obj);
if (is_alive_non_header()->do_object_b(java_lang_ref_Reference::referent(obj))) {
return false; // referent is reachable
}
}
{- -------------------------------------------
(1) もし処理対象の参照オブジェクトが Soft reference の場合は,
ReferencePolicy を使って, 差し先を消去すべきかどうかを判定してみる.
もし消去しなくていいなら, 普通に強参照として扱えばいいだけなので, ここでリターン.
(使用予定の ReferencePolicy オブジェクト (以下の _current_soft_ref_policy) について,
ReferencePolicy::should_clear_reference() で判定を行う.
false を返したら差し先は消さなくてよい, ということを意味する.)
---------------------------------------- -}
if (rt == REF_SOFT) {
// For soft refs we can decide now if these are not
// current candidates for clearing, in which case we
// can mark through them now, rather than delaying that
// to the reference-processing phase. Since all current
// time-stamp policies advance the soft-ref clock only
// at a major collection cycle, this is always currently
// accurate.
if (!_current_soft_ref_policy->should_clear_reference(obj)) {
return false;
}
}
{- -------------------------------------------
(1) もし処理対象の参照オブジェクトが既に誰かに見つかっていたら,
これ以上処理することはないので, ここでリターン.
(参照オブジェクトの discovered フィールドが NULL でなければ
誰かが既に処理済み, ということを表す)
なお, (普通は GC 中に同じオブジェクトを2回以上処理することはないはずなので)
これがどういうことかと言うと, 以下のような状況とのこと.
* ポリシーが ReferentBasedDiscovery の場合:
他の世代に対する GC 中に見つかっていた, というケース.
* ポリシーが ReferenceBasedDiscovery の場合:
Concurrent に動作していた GC スレッドが見つけていた, というケース (というわけで, CMS か G1GC でないと起こりえない).
(ついでに, このケースでリターンする場合は
リターン直前に(トレース出力)もしている)
---------------------------------------- -}
HeapWord* const discovered_addr = java_lang_ref_Reference::discovered_addr(obj);
const oop discovered = java_lang_ref_Reference::discovered(obj);
assert(discovered->is_oop_or_null(), "bad discovered field");
if (discovered != NULL) {
// The reference has already been discovered...
{- -------------------------------------------
(1.1) (トレース出力)
---------------------------------------- -}
if (TraceReferenceGC) {
gclog_or_tty->print_cr("Already enqueued reference (" INTPTR_FORMAT ": %s)",
obj, obj->blueprint()->internal_name());
}
if (RefDiscoveryPolicy == ReferentBasedDiscovery) {
// assumes that an object is not processed twice;
// if it's been already discovered it must be on another
// generation's discovered list; so we won't discover it.
return false;
} else {
assert(RefDiscoveryPolicy == ReferenceBasedDiscovery,
"Unrecognized policy");
// Check assumption that an object is not potentially
// discovered twice except by concurrent collectors that potentially
// trace the same Reference object twice.
assert(UseConcMarkSweepGC || UseG1GC,
"Only possible with a concurrent marking collector");
return true;
}
}
{- -------------------------------------------
(1) ポリシーが ReferentBasedDiscovery の場合でも,
以下のどちらかが成り立っていないと, ここでリターン
* 参照オブジェクトが, 処理対象の領域内(span 内)にある
* この discovery 処理がアトミックに行われており (= CMS や G1GC のように並行して動いている GC スレッドがいない), かつ
参照オブジェクトの指し先が処理対象の領域内(span 内)にある
(なお, ポリシーが ReferenceBasedDiscovery の場合には,
一つ目の「参照オブジェクトが処理対象の領域内(span 内)にある」という条件が満たされなければリターン.
See above.)
---------------------------------------- -}
if (RefDiscoveryPolicy == ReferentBasedDiscovery) {
verify_referent(obj);
// enqueue if and only if either:
// reference is in our span or
// we are an atomic collector and referent is in our span
if (_span.contains(obj_addr) ||
(discovery_is_atomic() &&
_span.contains(java_lang_ref_Reference::referent(obj)))) {
// should_enqueue = true;
} else {
return false;
}
} else {
assert(RefDiscoveryPolicy == ReferenceBasedDiscovery &&
_span.contains(obj_addr), "code inconsistency");
}
{- -------------------------------------------
(1) ReferenceProcessor::get_discovered_list() で,
処理対象の参照オブジェクトをつなぐための適切な DiscoveredList を取得する.
(なお, ReferenceProcessor::get_discovered_list() が NULL を返したら
ここでリターンすることになっている.
ただし, NULL になるのは種別が REF_OTHER の場合だけで,
これは java.lang.ref.Reference のサブクラスであって
soft/weak/phantom/final のいずれでもないもの, の場合.
<= JavaSE の標準クラスにはそんなものはない(??)ので,
自分で Reference のサブクラスを作った場合のみありうる?? #TODO)
---------------------------------------- -}
// Get the right type of discovered queue head.
DiscoveredList* list = get_discovered_list(rt);
if (list == NULL) {
return false; // nothing special needs to be done
}
{- -------------------------------------------
(1) (以下の if 文内で, 処理対象の参照オブジェクトを DiscoveredList につなぐ作業を行う.
なお, discover 処理がマルチスレッドで行われているか(_discovery_is_mt が true かどうか)に応じて
2通りに分岐しており, マルチスレッド処理の場合には
実際の処理は ReferenceProcessor::add_to_discovered_list_mt() の中で行われる.
また, コンストラクタ引数で
write barrier の実行が要求されている場合(_discovered_list_needs_barrier が true の場合)には,
参照オブジェクトの discovered フィールドに他の参照オブジェクトへのポインタを書き込む際に
あわせて barrier set の更新処理も行う.)
---------------------------------------- -}
if (_discovery_is_mt) {
add_to_discovered_list_mt(*list, obj, discovered_addr);
} else {
// If "_discovered_list_needs_barrier", we do write barriers when
// updating the discovered reference list. Otherwise, we do a raw store
// here: the field will be visited later when processing the discovered
// references.
oop current_head = list->head();
{- -------------------------------------------
(1.1) write barrier の実行が要求されている場合(_discovered_list_needs_barrier が true の場合)であっても
G1GC の場合は BarrierSet::write_ref_field_pre() の実行までは必要ない.
(というのは, 書き込む前の discovered フィールドの値が NULL だから)
ReferenceProcessor::add_to_discovered_list_mt() 内のコメントによると,
「将来の GC アルゴリズムで pre-barrier を必要とするものが出てくるかもしれないので, この if 文を書いてある」とのこと.
(現状だと, そもそも _discovered_list_needs_barrier が true になるのが G1GC の場合しかないような...)
---------------------------------------- -}
// As in the case further above, since we are over-writing a NULL
// pre-value, we can safely elide the pre-barrier here for the case of G1.
assert(discovered == NULL, "control point invariant");
if (_discovered_list_needs_barrier && !UseG1GC) { // safe to elide for G1
if (UseCompressedOops) {
_bs->write_ref_field_pre((narrowOop*)discovered_addr, current_head);
} else {
_bs->write_ref_field_pre((oop*)discovered_addr, current_head);
}
guarantee(false, "Need to check non-G1 collector");
}
{- -------------------------------------------
(1.1) 処理対象の参照オブジェクトの discovered フィールドに,
現在リストの先頭になっている参照オブジェクトを書き込む
---------------------------------------- -}
oop_store_raw(discovered_addr, current_head);
{- -------------------------------------------
(1.1) write barrier の実行が要求されている場合(_discovered_list_needs_barrier が true の場合)には
BarrierSet::write_ref_field() を呼び出して barrier set を書き換えておく.
---------------------------------------- -}
if (_discovered_list_needs_barrier) {
_bs->write_ref_field((void*)discovered_addr, current_head);
}
{- -------------------------------------------
(1.1) リストの先頭を, 今回処理した参照オブジェクトに置き換える.
(ついでに, リストの長さをインクリメントしておく)
---------------------------------------- -}
list->set_head(obj);
list->inc_length(1);
{- -------------------------------------------
(1.1) (トレース出力)
---------------------------------------- -}
if (TraceReferenceGC) {
gclog_or_tty->print_cr("Enqueued reference (" INTPTR_FORMAT ": %s)",
obj, obj->blueprint()->internal_name());
}
}
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(obj->is_oop(), "Enqueued a bad reference");
{- -------------------------------------------
(1) (verify)
---------------------------------------- -}
verify_referent(obj);
{- -------------------------------------------
(1) リターン
---------------------------------------- -}
return true;
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.