hotspot/src/share/vm/runtime/biasedLocking.cpp
BiasedLocking::Condition BiasedLocking::revoke_and_rebias(Handle obj, bool attempt_rebias, TRAPS) {
{- -------------------------------------------
(1) (assert)
---------------------------------------- -}
assert(!SafepointSynchronize::is_at_safepoint(), "must not be called while at safepoint");
{- -------------------------------------------
(1) まずは,「対象のオブジェクトが anonymously biased な状態であり, 引数で rebias はしないと指定された」ケースの処理.
(なお, このケースには hashcode を埋めたくなった時とかによく来る, とのこと)
この場合は, mark フィールドを CAS で非 biased locking なパターンに置き換える.
(非 biased locking なパターンとは, markOopDesc::prototype() が返す CAS-based scheme の mark パターン)
(ただし, age フィールドだけは情報が消えるともったいないので, 現在の age フィールドの値で入れ替えておく)
CAS が成功したらここでリターン.
CAS が失敗したら, (他のスレッドが自分に biased してしまったということなので) revoke 処理へとフォールスルー.
(なお, この場合には heuristics を update しないが,
これは unwanted bulk revocation を引き起こさないようにするため, とのこと.
まぁ, 確かに Hashcode 埋めたみたいな(ある意味特殊な)話が rebias 発生頻度にカウントされても困るだろう.
<= いや, Hashcode をよく埋められるクラスというのがいたらどうする?? #TODO)
---------------------------------------- -}
// We can revoke the biases of anonymously-biased objects
// efficiently enough that we should not cause these revocations to
// update the heuristics because doing so may cause unwanted bulk
// revocations (which are expensive) to occur.
markOop mark = obj->mark();
if (mark->is_biased_anonymously() && !attempt_rebias) {
// We are probably trying to revoke the bias of this object due to
// an identity hash code computation. Try to revoke the bias
// without a safepoint. This is possible if we can successfully
// compare-and-exchange an unbiased header into the mark word of
// the object, meaning that no other thread has raced to acquire
// the bias of the object.
markOop biased_value = mark;
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED;
}
{- -------------------------------------------
(1) 次に,「対象のオブジェクトの mark フィールドは biased pattern をしているが,
そのクラス自体が bulk revoke されてしまっている」というケースの処理.
この場合は, CAS でオブジェクトの mark フィールドを非 biased locking pattern に置き換える.
(なお, 処理としては prototype header の値に置き換えるだけ.
bulk revoke 後なので, prototype header には非 biased locking 用 (CAS-based scheme 用) の mark の値が入っている)
CAS が成功しても失敗してもここでリターン.
(失敗した場合は, 他のスレッドが revoke してくれたということなので, 成功した場合と結果は変わらない).
(なお, この場合は heuristics を update しない.
というか, もう bulk revoke されて biased 不適格になってしまっているので update しても無意味.)
---------------------------------------- -}
} else if (mark->has_bias_pattern()) {
Klass* k = Klass::cast(obj->klass());
markOop prototype_header = k->prototype_header();
if (!prototype_header->has_bias_pattern()) {
// This object has a stale bias from before the bulk revocation
// for this data type occurred. It's pointless to update the
// heuristics at this point so simply update the header with a
// CAS. If we fail this race, the object's bias has been revoked
// by another thread so we simply return and let the caller deal
// with it.
markOop biased_value = mark;
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(prototype_header, obj->mark_addr(), mark);
assert(!(*(obj->mark_addr()))->has_bias_pattern(), "even if we raced, should still be revoked");
return BIAS_REVOKED;
{- -------------------------------------------
(1) 次に,「対象のオブジェクトの mark フィールドは biased pattern をしている(上の if 参照)が,
bulk rebias によって epoch がずれてしまっている」というケースの処理.
この場合は, 以下の用に処理を行う.
* 引数で rebias する(attempt_rebias = true)と指定されていた場合には, CAS で自分に biased させる.
(より具体的には, 対象のオブジェクトの mark フィールドを
「現在の prototype header の値にカレントスレッドを指すポインタを足し込んだ値」で置き換える)
* 引数で rebias はしない(attempt_rebias = false)と指定されていた場合には, CAS で revoke する.
(より具体的には, 対象のオブジェクトの mark フィールドを
「CAS-based scheme 用の mark の値(非 biased locking 用の mark 値)」で置き換える)
CAS が成功すれば, ここでリターン.
失敗すればこのままフォールスルー.
(なお, このケースについてはアセンブリコード内で処理されるのが普通だが,
ランタイム内で revoke が必要になった場合にはここにくることがある, とのこと)
---------------------------------------- -}
} else if (prototype_header->bias_epoch() != mark->bias_epoch()) {
// The epoch of this biasing has expired indicating that the
// object is effectively unbiased. Depending on whether we need
// to rebias or revoke the bias of this object we can do it
// efficiently enough with a CAS that we shouldn't update the
// heuristics. This is normally done in the assembly code but we
// can reach this point due to various points in the runtime
// needing to revoke biases.
if (attempt_rebias) {
assert(THREAD->is_Java_thread(), "");
markOop biased_value = mark;
markOop rebiased_prototype = markOopDesc::encode((JavaThread*) THREAD, mark->age(), prototype_header->bias_epoch());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(rebiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED_AND_REBIASED;
}
} else {
markOop biased_value = mark;
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
markOop res_mark = (markOop) Atomic::cmpxchg_ptr(unbiased_prototype, obj->mark_addr(), mark);
if (res_mark == biased_value) {
return BIAS_REVOKED;
}
}
}
}
{- -------------------------------------------
(1) (ここに到達するのは, 上記のどれでもない場合, あるいは上記のケースに当てはまったが CAS が失敗した場合)
---------------------------------------- -}
{- -------------------------------------------
(1) まず update_heuristics() を呼んで, heuristics 情報を更新し, どうすべきかを決める.
---------------------------------------- -}
HeuristicsResult heuristics = update_heuristics(obj(), attempt_rebias);
{- -------------------------------------------
(1) heuristics の結果が HR_NOT_BIASED であれば, ここでリターン.
(対象のオブジェクトの mark フィールドが biased locking pattern ではないということなので,
revoke したいのならもう済んでいるし, rebias はもうしようがないので, この関数でやることはもうない).
---------------------------------------- -}
if (heuristics == HR_NOT_BIASED) {
return NOT_BIASED;
{- -------------------------------------------
(1) heuristics の結果が HR_SINGLE_REVOKE であれば, 対象のオブジェクトを revoke させる.
その場合, 自分に biased されているかどうかで処理を分ける.
(なお, 自分に biased しているものを revoke するのは, 上で書いたケースと同様, Hashcode を産めたくなったときなどによく起こるとのこと)
* 自分に biased されている場合は, 単に revoke_bias() を呼び出すだけでいい.
(自分のスタックを見るだけでいいので safepoint 停止する必要がない.
仮に他のスレッドが同時に revoke したとしても, revoke 処理中に safepoint 停止点がないので, やっぱり contention しない.)
* そうでなければ, safepoint 停止して revoke させる.
---------------------------------------- -}
} else if (heuristics == HR_SINGLE_REVOKE) {
Klass *k = Klass::cast(obj->klass());
markOop prototype_header = k->prototype_header();
{- -------------------------------------------
(1.1) (こちらが, 自分に biased されているケースの処理.
revoke_bias() を呼び出してリターンする.)
---------------------------------------- -}
if (mark->biased_locker() == THREAD &&
prototype_header->bias_epoch() == mark->bias_epoch()) {
// A thread is trying to revoke the bias of an object biased
// toward it, again likely due to an identity hash code
// computation. We can again avoid a safepoint in this case
// since we are only going to walk our own stack. There are no
// races with revocations occurring in other threads because we
// reach no safepoints in the revocation path.
// Also check the epoch because even if threads match, another thread
// can come in with a CAS to steal the bias of an object that has a
// stale epoch.
ResourceMark rm;
if (TraceBiasedLocking) {
tty->print_cr("Revoking bias by walking my own stack:");
}
BiasedLocking::Condition cond = revoke_bias(obj(), false, false, (JavaThread*) THREAD);
((JavaThread*) THREAD)->set_cached_monitor_info(NULL);
assert(cond == BIAS_REVOKED, "why not?");
return cond;
{- -------------------------------------------
(1.1) (こちらが, 自分に biased されていないケースの処理.
VM_RevokeBias::doit() を呼び出す.)
---------------------------------------- -}
} else {
VM_RevokeBias revoke(&obj, (JavaThread*) THREAD);
VMThread::execute(&revoke);
return revoke.status_code();
}
}
{- -------------------------------------------
(1) heuristics の結果が HR_BULK_REVOKE か HR_BULK_REBIAS であれば,
VM_BulkRevokeBias を使って bulk revoke/bulk rebias を起こし, 結果をリターン.
---------------------------------------- -}
assert((heuristics == HR_BULK_REVOKE) ||
(heuristics == HR_BULK_REBIAS), "?");
VM_BulkRevokeBias bulk_revoke(&obj, (JavaThread*) THREAD,
(heuristics == HR_BULK_REBIAS),
attempt_rebias);
VMThread::execute(&bulk_revoke);
return bulk_revoke.status_code();
}
This document is available under the GNU GENERAL PUBLIC LICENSE Version 2.