Ответ 1
Он представляет резервную блокировку, которая была удалена в байт-коде.
Я всегда находил исходный код хорошим местом, чтобы начать с таких вещей. В обзоре hotspot/src/share/vm/opto/callnode.cpp
от openJDK были следующие интересные комментарии:
// Redundant lock elimination
//
// There are various patterns of locking where we release and
// immediately reacquire a lock in a piece of code where no operations
// occur in between that would be observable. In those cases we can
// skip releasing and reacquiring the lock without violating any
// fairness requirements. Doing this around a loop could cause a lock
// to be held for a very long time so we concentrate on non-looping
// control flow. We also require that the operations are fully
// redundant meaning that we don't introduce new lock operations on
// some paths so to be able to eliminate it on others ala PRE. This
// would probably require some more extensive graph manipulation to
// guarantee that the memory edges were all handled correctly.
//
// Assuming p is a simple predicate which can't trap in any way and s
// is a synchronized method consider this code:
//
// s();
// if (p)
// s();
// else
// s();
// s();
//
// 1. The unlocks of the first call to s can be eliminated if the
// locks inside the then and else branches are eliminated.
//
// 2. The unlocks of the then and else branches can be eliminated if
// the lock of the final call to s is eliminated.
//
// Either of these cases subsumes the simple case of sequential control flow
Таким образом, из приведенного выше, казалось бы, (по крайней мере, в openJDK), который устранен, означает, что блокировка поддерживается JVM через один или несколько наборов инструкций release/получения.
Просмотр javaVFrame::print_lock_info_on()
в hotspot/src/share/vm/runtime/vframe.cpp
показывает, где происходит проверка и вывод:
// Print out all monitors that we have locked or are trying to lock
GrowableArray<MonitorInfo*>* mons = monitors();
if (!mons->is_empty()) {
bool found_first_monitor = false;
for (int index = (mons->length()-1); index >= 0; index--) {
MonitorInfo* monitor = mons->at(index);
if (monitor->eliminated() && is_compiled_frame()) { // Eliminated in compiled code
if (monitor->owner_is_scalar_replaced()) {
Klass* k = Klass::cast(monitor->owner_klass());
st->print("\t- eliminated <owner is scalar replaced> (a %s)", k->external_name());
} else {
oop obj = monitor->owner();
if (obj != NULL) {
print_locked_object_class_name(st, obj, "eliminated");
}
}
continue;
Другие комментарии, в том числе выше, также ссылаются на замену инструкций блокировки и разблокировки с помощью NOP.
Я прочитал документ, который Dirk упомянул в отношении lock elision, и, похоже, это блокировка Coarsening, а не Elision:
Другая оптимизация, которая может быть использована для снижения стоимости блокировки, - это укрупнение замков. Блокировка огрубления - это процесс объединения соседних синхронизированных блоков, которые используют один и тот же объект блокировки. Если компилятор не может устранить блокировку с помощью блокировки, он может уменьшить накладные расходы за счет укрупнения блокировки.
Но, если честно, разница очень тонкая, и конечный эффект почти такой же - вы устраняете ненужные блокировки и разблокировки.