8315884: New Object to ObjectMonitor mapping

Co-authored-by: Erik Österlund <eosterlund@openjdk.org>
Co-authored-by: Stefan Karlsson <stefank@openjdk.org>
Co-authored-by: Coleen Phillimore <coleenp@openjdk.org>
Reviewed-by: rkennke, coleenp, dcubed
This commit is contained in:
Axel Boldt-Christmas 2024-08-16 06:20:17 +00:00
parent 74066bcca8
commit bd4160cea8
68 changed files with 3263 additions and 890 deletions

View File

@ -15793,7 +15793,7 @@ instruct cmpFastLockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNoSp
format %{ "fastlock $object,$box\t! kills $tmp,$tmp2,$tmp3" %}
ins_encode %{
__ fast_lock_lightweight($object$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register);
__ fast_lock_lightweight($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register);
%}
ins_pipe(pipe_serial);
@ -15809,7 +15809,7 @@ instruct cmpFastUnlockLightweight(rFlagsReg cr, iRegP object, iRegP box, iRegPNo
format %{ "fastunlock $object,$box\t! kills $tmp, $tmp2, $tmp3" %}
ins_encode %{
__ fast_unlock_lightweight($object$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register);
__ fast_unlock_lightweight($object$$Register, $box$$Register, $tmp$$Register, $tmp2$$Register, $tmp3$$Register);
%}
ins_pipe(pipe_serial);

View File

@ -81,7 +81,7 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr
}
if (LockingMode == LM_LIGHTWEIGHT) {
lightweight_lock(obj, hdr, temp, rscratch2, slow_case);
lightweight_lock(disp_hdr, obj, hdr, temp, rscratch2, slow_case);
} else if (LockingMode == LM_LEGACY) {
Label done;
// Load object header

View File

@ -224,10 +224,10 @@ void C2_MacroAssembler::fast_unlock(Register objectReg, Register boxReg, Registe
bind(no_count);
}
void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Register t1,
Register t2, Register t3) {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
assert_different_registers(obj, t1, t2, t3);
assert_different_registers(obj, box, t1, t2, t3);
// Handle inflated monitor.
Label inflated;
@ -236,6 +236,11 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
// Finish fast lock unsuccessfully. MUST branch to with flag == NE
Label slow_path;
if (UseObjectMonitorTable) {
// Clear cache in case fast locking succeeds.
str(zr, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
}
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(t1, obj);
ldrw(t1, Address(t1, Klass::access_flags_offset()));
@ -244,6 +249,7 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
}
const Register t1_mark = t1;
const Register t3_t = t3;
{ // Lightweight locking
@ -251,7 +257,6 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
Label push;
const Register t2_top = t2;
const Register t3_t = t3;
// Check if lock-stack is full.
ldrw(t2_top, Address(rthread, JavaThread::lock_stack_top_offset()));
@ -289,26 +294,71 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
{ // Handle inflated monitor.
bind(inflated);
// mark contains the tagged ObjectMonitor*.
const Register t1_tagged_monitor = t1_mark;
const uintptr_t monitor_tag = markWord::monitor_value;
const Register t1_monitor = t1;
if (!UseObjectMonitorTable) {
assert(t1_monitor == t1_mark, "should be the same here");
} else {
Label monitor_found;
// Load cache address
lea(t3_t, Address(rthread, JavaThread::om_cache_oops_offset()));
const int num_unrolled = 2;
for (int i = 0; i < num_unrolled; i++) {
ldr(t1, Address(t3_t));
cmp(obj, t1);
br(Assembler::EQ, monitor_found);
increment(t3_t, in_bytes(OMCache::oop_to_oop_difference()));
}
Label loop;
// Search for obj in cache.
bind(loop);
// Check for match.
ldr(t1, Address(t3_t));
cmp(obj, t1);
br(Assembler::EQ, monitor_found);
// Search until null encountered, guaranteed _null_sentinel at end.
increment(t3_t, in_bytes(OMCache::oop_to_oop_difference()));
cbnz(t1, loop);
// Cache Miss, NE set from cmp above, cbnz does not set flags
b(slow_path);
bind(monitor_found);
ldr(t1_monitor, Address(t3_t, OMCache::oop_to_monitor_difference()));
}
const Register t2_owner_addr = t2;
const Register t3_owner = t3;
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast<int>(markWord::monitor_value));
const Address owner_address(t1_monitor, ObjectMonitor::owner_offset() - monitor_tag);
const Address recursions_address(t1_monitor, ObjectMonitor::recursions_offset() - monitor_tag);
Label monitor_locked;
// Compute owner address.
lea(t2_owner_addr, Address(t1_tagged_monitor, (in_bytes(ObjectMonitor::owner_offset()) - monitor_tag)));
lea(t2_owner_addr, owner_address);
// CAS owner (null => current thread).
cmpxchg(t2_owner_addr, zr, rthread, Assembler::xword, /*acquire*/ true,
/*release*/ false, /*weak*/ false, t3_owner);
br(Assembler::EQ, locked);
br(Assembler::EQ, monitor_locked);
// Check if recursive.
cmp(t3_owner, rthread);
br(Assembler::NE, slow_path);
// Recursive.
increment(Address(t1_tagged_monitor, in_bytes(ObjectMonitor::recursions_offset()) - monitor_tag), 1);
increment(recursions_address, 1);
bind(monitor_locked);
if (UseObjectMonitorTable) {
str(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
}
}
bind(locked);
@ -331,13 +381,13 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register t1,
// C2 uses the value of Flags (NE vs EQ) to determine the continuation.
}
void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Register t2,
Register t3) {
void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register box, Register t1,
Register t2, Register t3) {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
assert_different_registers(obj, t1, t2, t3);
assert_different_registers(obj, box, t1, t2, t3);
// Handle inflated monitor.
Label inflated, inflated_load_monitor;
Label inflated, inflated_load_mark;
// Finish fast unlock successfully. MUST branch to with flag == EQ
Label unlocked;
// Finish fast unlock unsuccessfully. MUST branch to with flag == NE
@ -349,13 +399,15 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
{ // Lightweight unlock
Label push_and_slow_path;
// Check if obj is top of lock-stack.
ldrw(t2_top, Address(rthread, JavaThread::lock_stack_top_offset()));
subw(t2_top, t2_top, oopSize);
ldr(t3_t, Address(rthread, t2_top));
cmp(obj, t3_t);
// Top of lock stack was not obj. Must be monitor.
br(Assembler::NE, inflated_load_monitor);
br(Assembler::NE, inflated_load_mark);
// Pop lock-stack.
DEBUG_ONLY(str(zr, Address(rthread, t2_top));)
@ -372,7 +424,10 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
ldr(t1_mark, Address(obj, oopDesc::mark_offset_in_bytes()));
// Check header for monitor (0b10).
tbnz(t1_mark, exact_log2(markWord::monitor_value), inflated);
// Because we got here by popping (meaning we pushed in locked)
// there will be no monitor in the box. So we need to push back the obj
// so that the runtime can fix any potential anonymous owner.
tbnz(t1_mark, exact_log2(markWord::monitor_value), UseObjectMonitorTable ? push_and_slow_path : inflated);
// Try to unlock. Transition lock bits 0b00 => 0b01
assert(oopDesc::mark_offset_in_bytes() == 0, "required to avoid lea");
@ -381,6 +436,7 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
/*acquire*/ false, /*release*/ true, /*weak*/ false, noreg);
br(Assembler::EQ, unlocked);
bind(push_and_slow_path);
// Compare and exchange failed.
// Restore lock-stack and handle the unlock in runtime.
DEBUG_ONLY(str(obj, Address(rthread, t2_top));)
@ -391,7 +447,7 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
{ // Handle inflated monitor.
bind(inflated_load_monitor);
bind(inflated_load_mark);
ldr(t1_mark, Address(obj, oopDesc::mark_offset_in_bytes()));
#ifdef ASSERT
tbnz(t1_mark, exact_log2(markWord::monitor_value), inflated);
@ -412,12 +468,19 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register t1, Regis
bind(check_done);
#endif
// mark contains the tagged ObjectMonitor*.
const Register t1_monitor = t1_mark;
const uintptr_t monitor_tag = markWord::monitor_value;
const Register t1_monitor = t1;
// Untag the monitor.
sub(t1_monitor, t1_mark, monitor_tag);
if (!UseObjectMonitorTable) {
assert(t1_monitor == t1_mark, "should be the same here");
// Untag the monitor.
add(t1_monitor, t1_mark, -(int)markWord::monitor_value);
} else {
ldr(t1_monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
// null check with Flags == NE, no valid pointer below alignof(ObjectMonitor*)
cmp(t1_monitor, checked_cast<uint8_t>(alignof(ObjectMonitor*)));
br(Assembler::LO, slow_path);
}
const Register t2_recursions = t2;
Label not_recursive;

View File

@ -39,8 +39,8 @@
void fast_lock(Register object, Register box, Register tmp, Register tmp2, Register tmp3);
void fast_unlock(Register object, Register box, Register tmp, Register tmp2);
// Code used by cmpFastLockLightweight and cmpFastUnlockLightweight mach instructions in .ad file.
void fast_lock_lightweight(Register object, Register t1, Register t2, Register t3);
void fast_unlock_lightweight(Register object, Register t1, Register t2, Register t3);
void fast_lock_lightweight(Register object, Register box, Register t1, Register t2, Register t3);
void fast_unlock_lightweight(Register object, Register box, Register t1, Register t2, Register t3);
void string_compare(Register str1, Register str2,
Register cnt1, Register cnt2, Register result,

View File

@ -696,7 +696,7 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
}
if (LockingMode == LM_LIGHTWEIGHT) {
lightweight_lock(obj_reg, tmp, tmp2, tmp3, slow_case);
lightweight_lock(lock_reg, obj_reg, tmp, tmp2, tmp3, slow_case);
b(count);
} else if (LockingMode == LM_LEGACY) {
// Load (object->mark() | 1) into swap_reg
@ -752,15 +752,9 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
bind(slow_case);
// Call the runtime routine for slow case
if (LockingMode == LM_LIGHTWEIGHT) {
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj),
obj_reg);
} else {
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
}
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
b(done);
bind(count);

View File

@ -6750,9 +6750,9 @@ void MacroAssembler::double_move(VMRegPair src, VMRegPair dst, Register tmp) {
// - obj: the object to be locked
// - t1, t2, t3: temporary registers, will be destroyed
// - slow: branched to if locking fails, absolute offset may larger than 32KB (imm14 encoding).
void MacroAssembler::lightweight_lock(Register obj, Register t1, Register t2, Register t3, Label& slow) {
void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register t1, Register t2, Register t3, Label& slow) {
assert(LockingMode == LM_LIGHTWEIGHT, "only used with new lightweight locking");
assert_different_registers(obj, t1, t2, t3, rscratch1);
assert_different_registers(basic_lock, obj, t1, t2, t3, rscratch1);
Label push;
const Register top = t1;
@ -6763,6 +6763,11 @@ void MacroAssembler::lightweight_lock(Register obj, Register t1, Register t2, Re
// instruction emitted as it is part of C1's null check semantics.
ldr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
if (UseObjectMonitorTable) {
// Clear cache in case fast locking succeeds.
str(zr, Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))));
}
// Check if the lock-stack is full.
ldrw(top, Address(rthread, JavaThread::lock_stack_top_offset()));
cmpw(top, (unsigned)LockStack::end_offset());

View File

@ -1639,7 +1639,7 @@ public:
// Code for java.lang.Thread::onSpinWait() intrinsic.
void spin_wait();
void lightweight_lock(Register obj, Register t1, Register t2, Register t3, Label& slow);
void lightweight_lock(Register basic_lock, Register obj, Register t1, Register t2, Register t3, Label& slow);
void lightweight_unlock(Register obj, Register t1, Register t2, Register t3, Label& slow);
private:

View File

@ -1811,7 +1811,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
__ br(Assembler::NE, slow_path_lock);
} else {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
__ lightweight_lock(obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock);
__ lightweight_lock(lock_reg, obj_reg, swap_reg, tmp, lock_tmp, slow_path_lock);
}
__ bind(count);
__ increment(Address(rthread, JavaThread::held_monitor_count_offset()));

View File

@ -985,15 +985,7 @@ void InterpreterMacroAssembler::lock_object(Register Rlock) {
bind(slow_case);
// Call the runtime routine for slow case
if (LockingMode == LM_LIGHTWEIGHT) {
// Pass oop, not lock, in fast lock case. call_VM wants R1 though.
push(R1);
mov(R1, Robj);
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj), R1);
pop(R1);
} else {
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), Rlock);
}
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), Rlock);
bind(done);
}
}

View File

@ -1043,11 +1043,7 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) {
// None of the above fast optimizations worked so we have to get into the
// slow case of monitor enter.
bind(slow_case);
if (LockingMode == LM_LIGHTWEIGHT) {
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj), object);
} else {
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor);
}
call_VM(noreg, CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter), monitor);
b(done);
// }
align(32, 12);

View File

@ -2804,32 +2804,39 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(ConditionRegister fla
{ // Handle inflated monitor.
bind(inflated);
// mark contains the tagged ObjectMonitor*.
const Register tagged_monitor = mark;
const uintptr_t monitor_tag = markWord::monitor_value;
const Register owner_addr = tmp2;
if (!UseObjectMonitorTable) {
// mark contains the tagged ObjectMonitor*.
const Register tagged_monitor = mark;
const uintptr_t monitor_tag = markWord::monitor_value;
const Register owner_addr = tmp2;
// Compute owner address.
addi(owner_addr, tagged_monitor, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag);
// Compute owner address.
addi(owner_addr, tagged_monitor, in_bytes(ObjectMonitor::owner_offset()) - monitor_tag);
// CAS owner (null => current thread).
cmpxchgd(/*flag=*/flag,
/*current_value=*/t,
/*compare_value=*/(intptr_t)0,
/*exchange_value=*/R16_thread,
/*where=*/owner_addr,
MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq,
MacroAssembler::cmpxchgx_hint_acquire_lock());
beq(flag, locked);
// CAS owner (null => current thread).
cmpxchgd(/*flag=*/flag,
/*current_value=*/t,
/*compare_value=*/(intptr_t)0,
/*exchange_value=*/R16_thread,
/*where=*/owner_addr,
MacroAssembler::MemBarRel | MacroAssembler::MemBarAcq,
MacroAssembler::cmpxchgx_hint_acquire_lock());
beq(flag, locked);
// Check if recursive.
cmpd(flag, t, R16_thread);
bne(flag, slow_path);
// Check if recursive.
cmpd(flag, t, R16_thread);
bne(flag, slow_path);
// Recursive.
ld(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
addi(tmp1, tmp1, 1);
std(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
// Recursive.
ld(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
addi(tmp1, tmp1, 1);
std(tmp1, in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), owner_addr);
} else {
// OMCache lookup not supported yet. Take the slowpath.
// Set flag to NE
crxor(flag, Assembler::equal, flag, Assembler::equal);
b(slow_path);
}
}
bind(locked);
@ -2943,49 +2950,56 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(ConditionRegister f
bind(check_done);
#endif
// mark contains the tagged ObjectMonitor*.
const Register monitor = mark;
const uintptr_t monitor_tag = markWord::monitor_value;
if (!UseObjectMonitorTable) {
// mark contains the tagged ObjectMonitor*.
const Register monitor = mark;
const uintptr_t monitor_tag = markWord::monitor_value;
// Untag the monitor.
subi(monitor, mark, monitor_tag);
// Untag the monitor.
subi(monitor, mark, monitor_tag);
const Register recursions = tmp2;
Label not_recursive;
const Register recursions = tmp2;
Label not_recursive;
// Check if recursive.
ld(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor);
addic_(recursions, recursions, -1);
blt(CCR0, not_recursive);
// Check if recursive.
ld(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor);
addic_(recursions, recursions, -1);
blt(CCR0, not_recursive);
// Recursive unlock.
std(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor);
crorc(CCR0, Assembler::equal, CCR0, Assembler::equal);
b(unlocked);
// Recursive unlock.
std(recursions, in_bytes(ObjectMonitor::recursions_offset()), monitor);
crorc(CCR0, Assembler::equal, CCR0, Assembler::equal);
b(unlocked);
bind(not_recursive);
bind(not_recursive);
Label release_;
const Register t2 = tmp2;
Label release_;
const Register t2 = tmp2;
// Check if the entry lists are empty.
ld(t, in_bytes(ObjectMonitor::EntryList_offset()), monitor);
ld(t2, in_bytes(ObjectMonitor::cxq_offset()), monitor);
orr(t, t, t2);
cmpdi(flag, t, 0);
beq(flag, release_);
// Check if the entry lists are empty.
ld(t, in_bytes(ObjectMonitor::EntryList_offset()), monitor);
ld(t2, in_bytes(ObjectMonitor::cxq_offset()), monitor);
orr(t, t, t2);
cmpdi(flag, t, 0);
beq(flag, release_);
// The owner may be anonymous and we removed the last obj entry in
// the lock-stack. This loses the information about the owner.
// Write the thread to the owner field so the runtime knows the owner.
std(R16_thread, in_bytes(ObjectMonitor::owner_offset()), monitor);
b(slow_path);
// The owner may be anonymous and we removed the last obj entry in
// the lock-stack. This loses the information about the owner.
// Write the thread to the owner field so the runtime knows the owner.
std(R16_thread, in_bytes(ObjectMonitor::owner_offset()), monitor);
b(slow_path);
bind(release_);
// Set owner to null.
release();
// t contains 0
std(t, in_bytes(ObjectMonitor::owner_offset()), monitor);
bind(release_);
// Set owner to null.
release();
// t contains 0
std(t, in_bytes(ObjectMonitor::owner_offset()), monitor);
} else {
// OMCache lookup not supported yet. Take the slowpath.
// Set flag to NE
crxor(flag, Assembler::equal, flag, Assembler::equal);
b(slow_path);
}
}
bind(unlocked);

View File

@ -323,25 +323,30 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register tmp1, Regis
{ // Handle inflated monitor.
bind(inflated);
// mark contains the tagged ObjectMonitor*.
const Register tmp1_tagged_monitor = tmp1_mark;
const uintptr_t monitor_tag = markWord::monitor_value;
const Register tmp2_owner_addr = tmp2;
const Register tmp3_owner = tmp3;
if (!UseObjectMonitorTable) {
// mark contains the tagged ObjectMonitor*.
const Register tmp1_tagged_monitor = tmp1_mark;
const uintptr_t monitor_tag = markWord::monitor_value;
const Register tmp2_owner_addr = tmp2;
const Register tmp3_owner = tmp3;
// Compute owner address.
la(tmp2_owner_addr, Address(tmp1_tagged_monitor, (in_bytes(ObjectMonitor::owner_offset()) - monitor_tag)));
// Compute owner address.
la(tmp2_owner_addr, Address(tmp1_tagged_monitor, (in_bytes(ObjectMonitor::owner_offset()) - monitor_tag)));
// CAS owner (null => current thread).
cmpxchg(/*addr*/ tmp2_owner_addr, /*expected*/ zr, /*new*/ xthread, Assembler::int64,
/*acquire*/ Assembler::aq, /*release*/ Assembler::relaxed, /*result*/ tmp3_owner);
beqz(tmp3_owner, locked);
// CAS owner (null => current thread).
cmpxchg(/*addr*/ tmp2_owner_addr, /*expected*/ zr, /*new*/ xthread, Assembler::int64,
/*acquire*/ Assembler::aq, /*release*/ Assembler::relaxed, /*result*/ tmp3_owner);
beqz(tmp3_owner, locked);
// Check if recursive.
bne(tmp3_owner, xthread, slow_path);
// Check if recursive.
bne(tmp3_owner, xthread, slow_path);
// Recursive.
increment(Address(tmp1_tagged_monitor, in_bytes(ObjectMonitor::recursions_offset()) - monitor_tag), 1, tmp2, tmp3);
// Recursive.
increment(Address(tmp1_tagged_monitor, in_bytes(ObjectMonitor::recursions_offset()) - monitor_tag), 1, tmp2, tmp3);
} else {
// OMCache lookup not supported yet. Take the slowpath.
j(slow_path);
}
}
bind(locked);
@ -453,49 +458,54 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register tmp1, Reg
bind(check_done);
#endif
// mark contains the tagged ObjectMonitor*.
const Register tmp1_monitor = tmp1_mark;
const uintptr_t monitor_tag = markWord::monitor_value;
if (!UseObjectMonitorTable) {
// mark contains the tagged ObjectMonitor*.
const Register tmp1_monitor = tmp1_mark;
const uintptr_t monitor_tag = markWord::monitor_value;
// Untag the monitor.
sub(tmp1_monitor, tmp1_mark, monitor_tag);
// Untag the monitor.
sub(tmp1_monitor, tmp1_mark, monitor_tag);
const Register tmp2_recursions = tmp2;
Label not_recursive;
const Register tmp2_recursions = tmp2;
Label not_recursive;
// Check if recursive.
ld(tmp2_recursions, Address(tmp1_monitor, ObjectMonitor::recursions_offset()));
beqz(tmp2_recursions, not_recursive);
// Check if recursive.
ld(tmp2_recursions, Address(tmp1_monitor, ObjectMonitor::recursions_offset()));
beqz(tmp2_recursions, not_recursive);
// Recursive unlock.
addi(tmp2_recursions, tmp2_recursions, -1);
sd(tmp2_recursions, Address(tmp1_monitor, ObjectMonitor::recursions_offset()));
j(unlocked);
// Recursive unlock.
addi(tmp2_recursions, tmp2_recursions, -1);
sd(tmp2_recursions, Address(tmp1_monitor, ObjectMonitor::recursions_offset()));
j(unlocked);
bind(not_recursive);
bind(not_recursive);
Label release;
const Register tmp2_owner_addr = tmp2;
Label release;
const Register tmp2_owner_addr = tmp2;
// Compute owner address.
la(tmp2_owner_addr, Address(tmp1_monitor, ObjectMonitor::owner_offset()));
// Compute owner address.
la(tmp2_owner_addr, Address(tmp1_monitor, ObjectMonitor::owner_offset()));
// Check if the entry lists are empty.
ld(t0, Address(tmp1_monitor, ObjectMonitor::EntryList_offset()));
ld(tmp3_t, Address(tmp1_monitor, ObjectMonitor::cxq_offset()));
orr(t0, t0, tmp3_t);
beqz(t0, release);
// Check if the entry lists are empty.
ld(t0, Address(tmp1_monitor, ObjectMonitor::EntryList_offset()));
ld(tmp3_t, Address(tmp1_monitor, ObjectMonitor::cxq_offset()));
orr(t0, t0, tmp3_t);
beqz(t0, release);
// The owner may be anonymous and we removed the last obj entry in
// the lock-stack. This loses the information about the owner.
// Write the thread to the owner field so the runtime knows the owner.
sd(xthread, Address(tmp2_owner_addr));
j(slow_path);
// The owner may be anonymous and we removed the last obj entry in
// the lock-stack. This loses the information about the owner.
// Write the thread to the owner field so the runtime knows the owner.
sd(xthread, Address(tmp2_owner_addr));
j(slow_path);
bind(release);
// Set owner to null.
membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
sd(zr, Address(tmp2_owner_addr));
bind(release);
// Set owner to null.
membar(MacroAssembler::LoadStore | MacroAssembler::StoreStore);
sd(zr, Address(tmp2_owner_addr));
} else {
// OMCache lookup not supported yet. Take the slowpath.
j(slow_path);
}
}
bind(unlocked);

View File

@ -792,15 +792,9 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg)
bind(slow_case);
// Call the runtime routine for slow case
if (LockingMode == LM_LIGHTWEIGHT) {
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj),
obj_reg);
} else {
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
}
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
j(done);
bind(count);

View File

@ -1072,16 +1072,9 @@ void InterpreterMacroAssembler::lock_object(Register monitor, Register object) {
// None of the above fast optimizations worked so we have to get into the
// slow case of monitor enter.
bind(slow_case);
if (LockingMode == LM_LIGHTWEIGHT) {
// for lightweight locking we need to use monitorenter_obj, see interpreterRuntime.cpp
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj),
object);
} else {
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
monitor);
}
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
monitor);
// }
bind(done);

View File

@ -6218,26 +6218,33 @@ void MacroAssembler::compiler_fast_lock_lightweight_object(Register obj, Registe
{ // Handle inflated monitor.
bind(inflated);
// mark contains the tagged ObjectMonitor*.
const Register tagged_monitor = mark;
const Register zero = tmp2;
if (!UseObjectMonitorTable) {
// mark contains the tagged ObjectMonitor*.
const Register tagged_monitor = mark;
const Register zero = tmp2;
// Try to CAS m->owner from null to current thread.
// If m->owner is null, then csg succeeds and sets m->owner=THREAD and CR=EQ.
// Otherwise, register zero is filled with the current owner.
z_lghi(zero, 0);
z_csg(zero, Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), tagged_monitor);
z_bre(locked);
// Try to CAS m->owner from null to current thread.
// If m->owner is null, then csg succeeds and sets m->owner=THREAD and CR=EQ.
// Otherwise, register zero is filled with the current owner.
z_lghi(zero, 0);
z_csg(zero, Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), tagged_monitor);
z_bre(locked);
// Check if recursive.
z_cgr(Z_thread, zero); // zero contains the owner from z_csg instruction
z_brne(slow_path);
// Check if recursive.
z_cgr(Z_thread, zero); // zero contains the owner from z_csg instruction
z_brne(slow_path);
// Recursive
z_agsi(Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 1ll);
z_cgr(zero, zero);
// z_bru(locked);
// Uncomment above line in the future, for now jump address is right next to us.
// Recursive
z_agsi(Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 1ll);
z_cgr(zero, zero);
// z_bru(locked);
// Uncomment above line in the future, for now jump address is right next to us.
} else {
// OMCache lookup not supported yet. Take the slowpath.
// Set flag to NE
z_ltgr(obj, obj);
z_bru(slow_path);
}
}
BLOCK_COMMENT("} handle_inflated_monitor_lightweight_locking");
@ -6364,42 +6371,49 @@ void MacroAssembler::compiler_fast_unlock_lightweight_object(Register obj, Regis
bind(check_done);
#endif // ASSERT
// mark contains the tagged ObjectMonitor*.
const Register monitor = mark;
if (!UseObjectMonitorTable) {
// mark contains the tagged ObjectMonitor*.
const Register monitor = mark;
NearLabel not_recursive;
const Register recursions = tmp2;
NearLabel not_recursive;
const Register recursions = tmp2;
// Check if recursive.
load_and_test_long(recursions, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
z_bre(not_recursive); // if 0 then jump, it's not recursive locking
// Check if recursive.
load_and_test_long(recursions, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
z_bre(not_recursive); // if 0 then jump, it's not recursive locking
// Recursive unlock
z_agsi(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), -1ll);
z_cgr(monitor, monitor); // set the CC to EQUAL
z_bru(unlocked);
// Recursive unlock
z_agsi(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), -1ll);
z_cgr(monitor, monitor); // set the CC to EQUAL
z_bru(unlocked);
bind(not_recursive);
bind(not_recursive);
NearLabel not_ok;
// Check if the entry lists are empty.
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
z_brne(not_ok);
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
z_brne(not_ok);
NearLabel not_ok;
// Check if the entry lists are empty.
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
z_brne(not_ok);
load_and_test_long(tmp2, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
z_brne(not_ok);
z_release();
z_stg(tmp2 /*=0*/, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
z_release();
z_stg(tmp2 /*=0*/, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
z_bru(unlocked); // CC = EQ here
z_bru(unlocked); // CC = EQ here
bind(not_ok);
bind(not_ok);
// The owner may be anonymous, and we removed the last obj entry in
// the lock-stack. This loses the information about the owner.
// Write the thread to the owner field so the runtime knows the owner.
z_stg(Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
z_bru(slow_path); // CC = NE here
// The owner may be anonymous, and we removed the last obj entry in
// the lock-stack. This loses the information about the owner.
// Write the thread to the owner field so the runtime knows the owner.
z_stg(Z_thread, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner), monitor);
z_bru(slow_path); // CC = NE here
} else {
// OMCache lookup not supported yet. Take the slowpath.
// Set flag to NE
z_ltgr(obj, obj);
z_bru(slow_path);
}
}
bind(unlocked);

View File

@ -66,11 +66,13 @@ int C1_MacroAssembler::lock_object(Register hdr, Register obj, Register disp_hdr
if (LockingMode == LM_LIGHTWEIGHT) {
#ifdef _LP64
const Register thread = r15_thread;
lightweight_lock(disp_hdr, obj, hdr, thread, tmp, slow_case);
#else
const Register thread = disp_hdr;
get_thread(thread);
// Implicit null check.
movptr(hdr, Address(obj, oopDesc::mark_offset_in_bytes()));
// Lacking registers and thread on x86_32. Always take slow path.
jmp(slow_case);
#endif
lightweight_lock(obj, hdr, thread, tmp, slow_case);
} else if (LockingMode == LM_LEGACY) {
Label done;
// Load object header
@ -139,10 +141,8 @@ void C1_MacroAssembler::unlock_object(Register hdr, Register obj, Register disp_
#ifdef _LP64
lightweight_unlock(obj, disp_hdr, r15_thread, hdr, slow_case);
#else
// This relies on the implementation of lightweight_unlock being able to handle
// that the reg_rax and thread Register parameters may alias each other.
get_thread(disp_hdr);
lightweight_unlock(obj, disp_hdr, disp_hdr, hdr, slow_case);
// Lacking registers and thread on x86_32. Always take slow path.
jmp(slow_case);
#endif
} else if (LockingMode == LM_LEGACY) {
// test if object header is pointing to the displaced header, and if so, restore

View File

@ -96,6 +96,7 @@ void C2FastUnlockLightweightStub::emit(C2_MacroAssembler& masm) {
{ // Restore held monitor count and slow path.
__ bind(restore_held_monitor_count_and_slow_path);
__ bind(_slow_path);
// Restore held monitor count.
__ increment(Address(_thread, JavaThread::held_monitor_count_offset()));
// increment will always result in ZF = 0 (no overflows).
@ -112,19 +113,23 @@ void C2FastUnlockLightweightStub::emit(C2_MacroAssembler& masm) {
#ifndef _LP64
__ jmpb(restore_held_monitor_count_and_slow_path);
#else // _LP64
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast<int>(markWord::monitor_value));
const Address succ_address(monitor, ObjectMonitor::succ_offset() - monitor_tag);
const Address owner_address(monitor, ObjectMonitor::owner_offset() - monitor_tag);
// successor null check.
__ cmpptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), NULL_WORD);
__ cmpptr(succ_address, NULL_WORD);
__ jccb(Assembler::equal, restore_held_monitor_count_and_slow_path);
// Release lock.
__ movptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD);
__ movptr(owner_address, NULL_WORD);
// Fence.
// Instead of MFENCE we use a dummy locked add of 0 to the top-of-stack.
__ lock(); __ addl(Address(rsp, 0), 0);
// Recheck successor.
__ cmpptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(succ)), NULL_WORD);
__ cmpptr(succ_address, NULL_WORD);
// Observed a successor after the release -> fence we have handed off the monitor
__ jccb(Assembler::notEqual, fix_zf_and_unlocked);
@ -133,7 +138,7 @@ void C2FastUnlockLightweightStub::emit(C2_MacroAssembler& masm) {
// not handle the monitor handoff. Currently only works
// due to the responsible thread.
__ xorptr(rax, rax);
__ lock(); __ cmpxchgptr(_thread, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)));
__ lock(); __ cmpxchgptr(_thread, owner_address);
__ jccb (Assembler::equal, restore_held_monitor_count_and_slow_path);
#endif

View File

@ -590,6 +590,11 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist
// Finish fast lock unsuccessfully. MUST jump with ZF == 0
Label slow_path;
if (UseObjectMonitorTable) {
// Clear cache in case fast locking succeeds.
movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), 0);
}
if (DiagnoseSyncOnValueBasedClasses != 0) {
load_klass(rax_reg, obj, t);
movl(rax_reg, Address(rax_reg, Klass::access_flags_offset()));
@ -603,7 +608,7 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist
Label push;
const Register top = box;
const Register top = UseObjectMonitorTable ? rax_reg : box;
// Load the mark.
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
@ -630,6 +635,10 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist
lock(); cmpxchgptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
jcc(Assembler::notEqual, slow_path);
if (UseObjectMonitorTable) {
// Need to reload top, clobbered by CAS.
movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
}
bind(push);
// After successful lock, push object on lock-stack.
movptr(Address(thread, top), obj);
@ -640,19 +649,68 @@ void C2_MacroAssembler::fast_lock_lightweight(Register obj, Register box, Regist
{ // Handle inflated monitor.
bind(inflated);
const Register tagged_monitor = mark;
const Register monitor = t;
if (!UseObjectMonitorTable) {
assert(mark == monitor, "should be the same here");
} else {
// Uses ObjectMonitorTable. Look for the monitor in the om_cache.
// Fetch ObjectMonitor* from the cache or take the slow-path.
Label monitor_found;
// Load cache address
lea(t, Address(thread, JavaThread::om_cache_oops_offset()));
const int num_unrolled = 2;
for (int i = 0; i < num_unrolled; i++) {
cmpptr(obj, Address(t));
jccb(Assembler::equal, monitor_found);
increment(t, in_bytes(OMCache::oop_to_oop_difference()));
}
Label loop;
// Search for obj in cache.
bind(loop);
// Check for match.
cmpptr(obj, Address(t));
jccb(Assembler::equal, monitor_found);
// Search until null encountered, guaranteed _null_sentinel at end.
cmpptr(Address(t), 1);
jcc(Assembler::below, slow_path); // 0 check, but with ZF=0 when *t == 0
increment(t, in_bytes(OMCache::oop_to_oop_difference()));
jmpb(loop);
// Cache hit.
bind(monitor_found);
movptr(monitor, Address(t, OMCache::oop_to_monitor_difference()));
}
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast<int>(markWord::monitor_value));
const Address recursions_address(monitor, ObjectMonitor::recursions_offset() - monitor_tag);
const Address owner_address(monitor, ObjectMonitor::owner_offset() - monitor_tag);
Label monitor_locked;
// Lock the monitor.
// CAS owner (null => current thread).
xorptr(rax_reg, rax_reg);
lock(); cmpxchgptr(thread, Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)));
jccb(Assembler::equal, locked);
lock(); cmpxchgptr(thread, owner_address);
jccb(Assembler::equal, monitor_locked);
// Check if recursive.
cmpptr(thread, rax_reg);
jccb(Assembler::notEqual, slow_path);
// Recursive.
increment(Address(tagged_monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
increment(recursions_address);
bind(monitor_locked);
if (UseObjectMonitorTable) {
// Cache the monitor for unlock
movptr(Address(box, BasicLock::object_monitor_cache_offset_in_bytes()), monitor);
}
}
bind(locked);
@ -694,7 +752,9 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
decrement(Address(thread, JavaThread::held_monitor_count_offset()));
const Register mark = t;
const Register top = reg_rax;
const Register monitor = t;
const Register top = UseObjectMonitorTable ? t : reg_rax;
const Register box = reg_rax;
Label dummy;
C2FastUnlockLightweightStub* stub = nullptr;
@ -706,14 +766,17 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
Label& push_and_slow_path = stub == nullptr ? dummy : stub->push_and_slow_path();
Label& check_successor = stub == nullptr ? dummy : stub->check_successor();
Label& slow_path = stub == nullptr ? dummy : stub->slow_path();
{ // Lightweight Unlock
// Load top.
movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
// Prefetch mark.
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
if (!UseObjectMonitorTable) {
// Prefetch mark.
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
}
// Check if obj is top of lock-stack.
cmpptr(obj, Address(thread, top, Address::times_1, -oopSize));
@ -730,6 +793,11 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
// We elide the monitor check, let the CAS fail instead.
if (UseObjectMonitorTable) {
// Load mark.
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
}
// Try to unlock. Transition lock bits 0b00 => 0b01
movptr(reg_rax, mark);
andptr(reg_rax, ~(int32_t)markWord::lock_mask);
@ -751,6 +819,9 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
jccb(Assembler::notEqual, inflated_check_lock_stack);
stop("Fast Unlock lock on stack");
bind(check_done);
if (UseObjectMonitorTable) {
movptr(mark, Address(obj, oopDesc::mark_offset_in_bytes()));
}
testptr(mark, markWord::monitor_value);
jccb(Assembler::notZero, inflated);
stop("Fast Unlock not monitor");
@ -758,43 +829,40 @@ void C2_MacroAssembler::fast_unlock_lightweight(Register obj, Register reg_rax,
bind(inflated);
// mark contains the tagged ObjectMonitor*.
const Register monitor = mark;
if (!UseObjectMonitorTable) {
assert(mark == monitor, "should be the same here");
} else {
// Uses ObjectMonitorTable. Look for the monitor in our BasicLock on the stack.
movptr(monitor, Address(box, BasicLock::object_monitor_cache_offset_in_bytes()));
// null check with ZF == 0, no valid pointer below alignof(ObjectMonitor*)
cmpptr(monitor, alignof(ObjectMonitor*));
jcc(Assembler::below, slow_path);
}
const ByteSize monitor_tag = in_ByteSize(UseObjectMonitorTable ? 0 : checked_cast<int>(markWord::monitor_value));
const Address recursions_address{monitor, ObjectMonitor::recursions_offset() - monitor_tag};
const Address cxq_address{monitor, ObjectMonitor::cxq_offset() - monitor_tag};
const Address EntryList_address{monitor, ObjectMonitor::EntryList_offset() - monitor_tag};
const Address owner_address{monitor, ObjectMonitor::owner_offset() - monitor_tag};
#ifndef _LP64
// Check if recursive.
xorptr(reg_rax, reg_rax);
orptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
jcc(Assembler::notZero, check_successor);
// Check if the entry lists are empty.
movptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
orptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
jcc(Assembler::notZero, check_successor);
// Release lock.
movptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD);
#else // _LP64
Label recursive;
// Check if recursive.
cmpptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)), 0);
cmpptr(recursions_address, 0);
jccb(Assembler::notEqual, recursive);
// Check if the entry lists are empty.
movptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(cxq)));
orptr(reg_rax, Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(EntryList)));
movptr(reg_rax, cxq_address);
orptr(reg_rax, EntryList_address);
jcc(Assembler::notZero, check_successor);
// Release lock.
movptr(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(owner)), NULL_WORD);
movptr(owner_address, NULL_WORD);
jmpb(unlocked);
// Recursive unlock.
bind(recursive);
decrement(Address(monitor, OM_OFFSET_NO_MONITOR_VALUE_TAG(recursions)));
decrement(recursions_address);
xorl(t, t);
#endif
}
bind(unlocked);

View File

@ -1183,11 +1183,11 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) {
if (LockingMode == LM_LIGHTWEIGHT) {
#ifdef _LP64
const Register thread = r15_thread;
lightweight_lock(lock_reg, obj_reg, swap_reg, thread, tmp_reg, slow_case);
#else
const Register thread = lock_reg;
get_thread(thread);
// Lacking registers and thread on x86_32. Always take slow path.
jmp(slow_case);
#endif
lightweight_lock(obj_reg, swap_reg, thread, tmp_reg, slow_case);
} else if (LockingMode == LM_LEGACY) {
// Load immediate 1 into swap_reg %rax
movl(swap_reg, 1);
@ -1249,15 +1249,9 @@ void InterpreterMacroAssembler::lock_object(Register lock_reg) {
bind(slow_case);
// Call the runtime routine for slow case
if (LockingMode == LM_LIGHTWEIGHT) {
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter_obj),
obj_reg);
} else {
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
}
call_VM(noreg,
CAST_FROM_FN_PTR(address, InterpreterRuntime::monitorenter),
lock_reg);
bind(done);
}
}
@ -1306,10 +1300,8 @@ void InterpreterMacroAssembler::unlock_object(Register lock_reg) {
#ifdef _LP64
lightweight_unlock(obj_reg, swap_reg, r15_thread, header_reg, slow_case);
#else
// This relies on the implementation of lightweight_unlock being able to handle
// that the reg_rax and thread Register parameters may alias each other.
get_thread(swap_reg);
lightweight_unlock(obj_reg, swap_reg, swap_reg, header_reg, slow_case);
// Lacking registers and thread on x86_32. Always take slow path.
jmp(slow_case);
#endif
} else if (LockingMode == LM_LEGACY) {
// Load the old header from BasicLock structure

View File

@ -10275,9 +10275,9 @@ void MacroAssembler::check_stack_alignment(Register sp, const char* msg, unsigne
// reg_rax: rax
// thread: the thread which attempts to lock obj
// tmp: a temporary register
void MacroAssembler::lightweight_lock(Register obj, Register reg_rax, Register thread, Register tmp, Label& slow) {
void MacroAssembler::lightweight_lock(Register basic_lock, Register obj, Register reg_rax, Register thread, Register tmp, Label& slow) {
assert(reg_rax == rax, "");
assert_different_registers(obj, reg_rax, thread, tmp);
assert_different_registers(basic_lock, obj, reg_rax, thread, tmp);
Label push;
const Register top = tmp;
@ -10286,6 +10286,11 @@ void MacroAssembler::lightweight_lock(Register obj, Register reg_rax, Register t
// instruction emitted as it is part of C1's null check semantics.
movptr(reg_rax, Address(obj, oopDesc::mark_offset_in_bytes()));
if (UseObjectMonitorTable) {
// Clear cache in case fast locking succeeds.
movptr(Address(basic_lock, BasicObjectLock::lock_offset() + in_ByteSize((BasicLock::object_monitor_cache_offset_in_bytes()))), 0);
}
// Load top.
movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
@ -10324,13 +10329,9 @@ void MacroAssembler::lightweight_lock(Register obj, Register reg_rax, Register t
// reg_rax: rax
// thread: the thread
// tmp: a temporary register
//
// x86_32 Note: reg_rax and thread may alias each other due to limited register
// availiability.
void MacroAssembler::lightweight_unlock(Register obj, Register reg_rax, Register thread, Register tmp, Label& slow) {
assert(reg_rax == rax, "");
assert_different_registers(obj, reg_rax, tmp);
LP64_ONLY(assert_different_registers(obj, reg_rax, thread, tmp);)
assert_different_registers(obj, reg_rax, thread, tmp);
Label unlocked, push_and_slow;
const Register top = tmp;
@ -10370,10 +10371,6 @@ void MacroAssembler::lightweight_unlock(Register obj, Register reg_rax, Register
bind(push_and_slow);
// Restore lock-stack and handle the unlock in runtime.
if (thread == reg_rax) {
// On x86_32 we may lose the thread.
get_thread(thread);
}
#ifdef ASSERT
movl(top, Address(thread, JavaThread::lock_stack_top_offset()));
movptr(Address(thread, top), obj);

View File

@ -2148,7 +2148,7 @@ public:
void check_stack_alignment(Register sp, const char* msg, unsigned bias = 0, Register tmp = noreg);
void lightweight_lock(Register obj, Register reg_rax, Register thread, Register tmp, Label& slow);
void lightweight_lock(Register basic_lock, Register obj, Register reg_rax, Register thread, Register tmp, Label& slow);
void lightweight_unlock(Register obj, Register reg_rax, Register thread, Register tmp, Label& slow);
#ifdef _LP64

View File

@ -62,9 +62,11 @@ void SharedRuntime::inline_check_hashcode_from_object_header(MacroAssembler* mas
if (LockingMode == LM_LIGHTWEIGHT) {
// check if monitor
__ testptr(result, markWord::monitor_value);
__ jcc(Assembler::notZero, slowCase);
if (!UseObjectMonitorTable) {
// check if monitor
__ testptr(result, markWord::monitor_value);
__ jcc(Assembler::notZero, slowCase);
}
} else {
// check if locked
__ testptr(result, markWord::unlocked_value);

View File

@ -1686,7 +1686,8 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
__ jcc(Assembler::notEqual, slow_path_lock);
} else {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
__ lightweight_lock(obj_reg, swap_reg, thread, lock_reg, slow_path_lock);
// Lacking registers and thread on x86_32. Always take slow path.
__ jmp(slow_path_lock);
}
__ bind(count_mon);
__ inc_held_monitor_count();

View File

@ -2266,7 +2266,7 @@ nmethod* SharedRuntime::generate_native_wrapper(MacroAssembler* masm,
__ jcc(Assembler::notEqual, slow_path_lock);
} else {
assert(LockingMode == LM_LIGHTWEIGHT, "must be");
__ lightweight_lock(obj_reg, swap_reg, r15_thread, rscratch1, slow_path_lock);
__ lightweight_lock(lock_reg, obj_reg, swap_reg, r15_thread, rscratch1, slow_path_lock);
}
__ bind(count_mon);
__ inc_held_monitor_count();

View File

@ -37,6 +37,7 @@
#include "oops/method.hpp"
#include "oops/oop.inline.hpp"
#include "prims/jvmtiExport.hpp"
#include "runtime/basicLock.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
@ -44,6 +45,7 @@
#include "runtime/timer.hpp"
#include "runtime/timerTrace.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
#include "entry_zero.hpp"
@ -331,23 +333,27 @@ int ZeroInterpreter::native_entry(Method* method, intptr_t UNUSED, TRAPS) {
if (method->is_synchronized()) {
monitor = (BasicObjectLock*) istate->stack_base();
oop lockee = monitor->obj();
markWord disp = lockee->mark().set_unlocked();
monitor->lock()->set_displaced_header(disp);
bool call_vm = (LockingMode == LM_MONITOR);
bool inc_monitor_count = true;
if (call_vm || lockee->cas_set_mark(markWord::from_pointer(monitor), disp) != disp) {
// Is it simple recursive case?
if (!call_vm && thread->is_lock_owned((address) disp.clear_lock_bits().to_pointer())) {
monitor->lock()->set_displaced_header(markWord::from_pointer(nullptr));
} else {
inc_monitor_count = false;
CALL_VM_NOCHECK(InterpreterRuntime::monitorenter(thread, monitor));
if (HAS_PENDING_EXCEPTION)
goto unwind_and_return;
bool success = false;
if (LockingMode == LM_LEGACY) {
markWord disp = lockee->mark().set_unlocked();
monitor->lock()->set_displaced_header(disp);
success = true;
if (lockee->cas_set_mark(markWord::from_pointer(monitor), disp) != disp) {
// Is it simple recursive case?
if (thread->is_lock_owned((address) disp.clear_lock_bits().to_pointer())) {
monitor->lock()->set_displaced_header(markWord::from_pointer(nullptr));
} else {
success = false;
}
}
if (success) {
THREAD->inc_held_monitor_count();
}
}
if (inc_monitor_count) {
THREAD->inc_held_monitor_count();
if (!success) {
CALL_VM_NOCHECK(InterpreterRuntime::monitorenter(thread, monitor));
if (HAS_PENDING_EXCEPTION)
goto unwind_and_return;
}
}

View File

@ -757,8 +757,8 @@ JRT_BLOCK_ENTRY(void, Runtime1::monitorenter(JavaThread* current, oopDesc* obj,
if (LockingMode == LM_MONITOR) {
lock->set_obj(obj);
}
assert(LockingMode == LM_LIGHTWEIGHT || obj == lock->obj(), "must match");
SharedRuntime::monitor_enter_helper(obj, LockingMode == LM_LIGHTWEIGHT ? nullptr : lock->lock(), current);
assert(obj == lock->obj(), "must match");
SharedRuntime::monitor_enter_helper(obj, lock->lock(), current);
JRT_END

View File

@ -71,7 +71,7 @@
#include "runtime/sharedRuntime.hpp"
#include "runtime/stackWatermarkSet.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/synchronizer.inline.hpp"
#include "runtime/threadCritical.hpp"
#include "utilities/align.hpp"
#include "utilities/checkedCast.hpp"
@ -725,7 +725,6 @@ void InterpreterRuntime::resolve_get_put(Bytecodes::Code bytecode, int field_ind
//%note monitor_1
JRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* current, BasicObjectLock* elem))
assert(LockingMode != LM_LIGHTWEIGHT, "Should call monitorenter_obj() when using the new lightweight locking");
#ifdef ASSERT
current->last_frame().interpreter_frame_verify_monitor(elem);
#endif
@ -740,23 +739,6 @@ JRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter(JavaThread* current, B
#endif
JRT_END
// NOTE: We provide a separate implementation for the new lightweight locking to workaround a limitation
// of registers in x86_32. This entry point accepts an oop instead of a BasicObjectLock*.
// The problem is that we would need to preserve the register that holds the BasicObjectLock,
// but we are using that register to hold the thread. We don't have enough registers to
// also keep the BasicObjectLock, but we don't really need it anyway, we only need
// the object. See also InterpreterMacroAssembler::lock_object().
// As soon as legacy stack-locking goes away we could remove the other monitorenter() entry
// point, and only use oop-accepting entries (same for monitorexit() below).
JRT_ENTRY_NO_ASYNC(void, InterpreterRuntime::monitorenter_obj(JavaThread* current, oopDesc* obj))
assert(LockingMode == LM_LIGHTWEIGHT, "Should call monitorenter() when not using the new lightweight locking");
Handle h_obj(current, cast_to_oop(obj));
assert(Universe::heap()->is_in_or_null(h_obj()),
"must be null or an object");
ObjectSynchronizer::enter(h_obj, nullptr, current);
return;
JRT_END
JRT_LEAF(void, InterpreterRuntime::monitorexit(BasicObjectLock* elem))
oop obj = elem->obj();
assert(Universe::heap()->is_in(obj), "must be an object");

View File

@ -53,7 +53,9 @@
#include "prims/jvmtiExport.hpp"
#include "prims/jvmtiThreadState.hpp"
#include "runtime/atomic.hpp"
#include "runtime/basicLock.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/globals.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/orderAccess.hpp"
@ -61,6 +63,7 @@
#include "runtime/threadCritical.hpp"
#include "utilities/debug.hpp"
#include "utilities/exceptions.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
/*
@ -624,23 +627,28 @@ void BytecodeInterpreter::run(interpreterState istate) {
BasicObjectLock* mon = &istate->monitor_base()[-1];
mon->set_obj(rcvr);
// Traditional lightweight locking.
markWord displaced = rcvr->mark().set_unlocked();
mon->lock()->set_displaced_header(displaced);
bool call_vm = (LockingMode == LM_MONITOR);
bool inc_monitor_count = true;
if (call_vm || rcvr->cas_set_mark(markWord::from_pointer(mon), displaced) != displaced) {
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
mon->lock()->set_displaced_header(markWord::from_pointer(nullptr));
} else {
inc_monitor_count = false;
CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception);
bool success = false;
if (LockingMode == LM_LEGACY) {
// Traditional fast locking.
markWord displaced = rcvr->mark().set_unlocked();
mon->lock()->set_displaced_header(displaced);
success = true;
if (rcvr->cas_set_mark(markWord::from_pointer(mon), displaced) != displaced) {
// Is it simple recursive case?
if (THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
mon->lock()->set_displaced_header(markWord::from_pointer(nullptr));
} else {
success = false;
}
}
if (success) {
THREAD->inc_held_monitor_count();
}
}
if (inc_monitor_count) {
THREAD->inc_held_monitor_count();
if (!success) {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, mon), handle_exception);
}
}
THREAD->set_do_not_unlock_if_synchronized(false);
@ -723,23 +731,28 @@ void BytecodeInterpreter::run(interpreterState istate) {
assert(entry->obj() == nullptr, "Frame manager didn't allocate the monitor");
entry->set_obj(lockee);
// traditional lightweight locking
markWord displaced = lockee->mark().set_unlocked();
entry->lock()->set_displaced_header(displaced);
bool call_vm = (LockingMode == LM_MONITOR);
bool inc_monitor_count = true;
if (call_vm || lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) {
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
entry->lock()->set_displaced_header(markWord::from_pointer(nullptr));
} else {
inc_monitor_count = false;
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
bool success = false;
if (LockingMode == LM_LEGACY) {
// Traditional fast locking.
markWord displaced = lockee->mark().set_unlocked();
entry->lock()->set_displaced_header(displaced);
success = true;
if (lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) {
// Is it simple recursive case?
if (THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
entry->lock()->set_displaced_header(markWord::from_pointer(nullptr));
} else {
success = false;
}
}
if (success) {
THREAD->inc_held_monitor_count();
}
}
if (inc_monitor_count) {
THREAD->inc_held_monitor_count();
if (!success) {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
UPDATE_PC_AND_TOS(1, -1);
goto run;
}
@ -1653,23 +1666,28 @@ run:
if (entry != nullptr) {
entry->set_obj(lockee);
// traditional lightweight locking
markWord displaced = lockee->mark().set_unlocked();
entry->lock()->set_displaced_header(displaced);
bool call_vm = (LockingMode == LM_MONITOR);
bool inc_monitor_count = true;
if (call_vm || lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) {
// Is it simple recursive case?
if (!call_vm && THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
entry->lock()->set_displaced_header(markWord::from_pointer(nullptr));
} else {
inc_monitor_count = false;
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
bool success = false;
if (LockingMode == LM_LEGACY) {
// Traditional fast locking.
markWord displaced = lockee->mark().set_unlocked();
entry->lock()->set_displaced_header(displaced);
success = true;
if (lockee->cas_set_mark(markWord::from_pointer(entry), displaced) != displaced) {
// Is it simple recursive case?
if (THREAD->is_lock_owned((address) displaced.clear_lock_bits().to_pointer())) {
entry->lock()->set_displaced_header(markWord::from_pointer(nullptr));
} else {
success = false;
}
}
if (success) {
THREAD->inc_held_monitor_count();
}
}
if (inc_monitor_count) {
THREAD->inc_held_monitor_count();
if (!success) {
CALL_VM(InterpreterRuntime::monitorenter(THREAD, entry), handle_exception);
}
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
} else {
istate->set_msg(more_monitors);
@ -1687,23 +1705,27 @@ run:
while (most_recent != limit ) {
if ((most_recent)->obj() == lockee) {
BasicLock* lock = most_recent->lock();
markWord header = lock->displaced_header();
most_recent->set_obj(nullptr);
// If it isn't recursive we either must swap old header or call the runtime
bool dec_monitor_count = true;
bool call_vm = (LockingMode == LM_MONITOR);
if (header.to_pointer() != nullptr || call_vm) {
markWord old_header = markWord::encode(lock);
if (call_vm || lockee->cas_set_mark(header, old_header) != old_header) {
// restore object for the slow case
most_recent->set_obj(lockee);
dec_monitor_count = false;
InterpreterRuntime::monitorexit(most_recent);
bool success = false;
if (LockingMode == LM_LEGACY) {
// If it isn't recursive we either must swap old header or call the runtime
most_recent->set_obj(nullptr);
success = true;
markWord header = lock->displaced_header();
if (header.to_pointer() != nullptr) {
markWord old_header = markWord::encode(lock);
if (lockee->cas_set_mark(header, old_header) != old_header) {
// restore object for the slow case
most_recent->set_obj(lockee);
success = false;
}
}
if (success) {
THREAD->dec_held_monitor_count();
}
}
if (dec_monitor_count) {
THREAD->dec_held_monitor_count();
if (!success) {
InterpreterRuntime::monitorexit(most_recent);
}
UPDATE_PC_AND_TOS_AND_CONTINUE(1, -1);
}
@ -3125,22 +3147,28 @@ run:
oop lockee = end->obj();
if (lockee != nullptr) {
BasicLock* lock = end->lock();
markWord header = lock->displaced_header();
end->set_obj(nullptr);
// If it isn't recursive we either must swap old header or call the runtime
bool dec_monitor_count = true;
if (header.to_pointer() != nullptr) {
markWord old_header = markWord::encode(lock);
if (lockee->cas_set_mark(header, old_header) != old_header) {
// restore object for the slow case
end->set_obj(lockee);
dec_monitor_count = false;
InterpreterRuntime::monitorexit(end);
bool success = false;
if (LockingMode == LM_LEGACY) {
markWord header = lock->displaced_header();
end->set_obj(nullptr);
// If it isn't recursive we either must swap old header or call the runtime
success = true;
if (header.to_pointer() != nullptr) {
markWord old_header = markWord::encode(lock);
if (lockee->cas_set_mark(header, old_header) != old_header) {
// restore object for the slow case
end->set_obj(lockee);
success = false;
}
}
if (success) {
THREAD->dec_held_monitor_count();
}
}
if (dec_monitor_count) {
THREAD->dec_held_monitor_count();
if (!success) {
InterpreterRuntime::monitorexit(end);
}
// One error is plenty
@ -3188,7 +3216,7 @@ run:
illegal_state_oop = Handle(THREAD, THREAD->pending_exception());
THREAD->clear_pending_exception();
}
} else if (LockingMode == LM_MONITOR) {
} else if (LockingMode != LM_LEGACY) {
InterpreterRuntime::monitorexit(base);
if (THREAD->has_pending_exception()) {
if (!suppress_error) illegal_state_oop = Handle(THREAD, THREAD->pending_exception());

View File

@ -151,7 +151,7 @@
nonstatic_field(Array<Klass*>, _length, int) \
nonstatic_field(Array<Klass*>, _data[0], Klass*) \
\
volatile_nonstatic_field(BasicLock, _displaced_header, markWord) \
volatile_nonstatic_field(BasicLock, _metadata, uintptr_t) \
\
static_field(CodeCache, _low_bound, address) \
static_field(CodeCache, _high_bound, address) \
@ -241,6 +241,7 @@
nonstatic_field(JavaThread, _stack_overflow_state._reserved_stack_activation, address) \
nonstatic_field(JavaThread, _held_monitor_count, intx) \
nonstatic_field(JavaThread, _lock_stack, LockStack) \
nonstatic_field(JavaThread, _om_cache, OMCache) \
JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_VTMS_transition, bool)) \
JVMTI_ONLY(nonstatic_field(JavaThread, _is_in_tmp_VTMS_transition, bool)) \
JVMTI_ONLY(nonstatic_field(JavaThread, _is_disable_suspend, bool)) \
@ -531,6 +532,8 @@
\
declare_constant_with_value("CardTable::dirty_card", CardTable::dirty_card_val()) \
declare_constant_with_value("LockStack::_end_offset", LockStack::end_offset()) \
declare_constant_with_value("OMCache::oop_to_oop_difference", OMCache::oop_to_oop_difference()) \
declare_constant_with_value("OMCache::oop_to_monitor_difference", OMCache::oop_to_monitor_difference()) \
\
declare_constant(CodeInstaller::VERIFIED_ENTRY) \
declare_constant(CodeInstaller::UNVERIFIED_ENTRY) \

View File

@ -130,6 +130,7 @@ class outputStream;
LOG_TAG(module) \
LOG_TAG(monitorinflation) \
LOG_TAG(monitormismatch) \
LOG_TAG(monitortable) \
LOG_TAG(native) \
LOG_TAG(nestmates) \
LOG_TAG(nmethod) \

View File

@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "oops/markWord.hpp"
#include "runtime/basicLock.inline.hpp"
#include "runtime/javaThread.hpp"
#include "runtime/objectMonitor.inline.hpp"
#include "utilities/ostream.hpp"
@ -67,7 +68,7 @@ void markWord::print_on(outputStream* st, bool print_monitor_info) const {
} else if (has_monitor()) { // last bits = 10
// have to check has_monitor() before is_locked()
st->print(" monitor(" INTPTR_FORMAT ")=", value());
if (print_monitor_info) {
if (print_monitor_info && !UseObjectMonitorTable) {
ObjectMonitor* mon = monitor();
if (mon == nullptr) {
st->print("null (this should never be seen!)");

View File

@ -197,13 +197,17 @@ class markWord {
}
ObjectMonitor* monitor() const {
assert(has_monitor(), "check");
assert(!UseObjectMonitorTable, "Lightweight locking with OM table does not use markWord for monitors");
// Use xor instead of &~ to provide one extra tag-bit check.
return (ObjectMonitor*) (value() ^ monitor_value);
}
bool has_displaced_mark_helper() const {
intptr_t lockbits = value() & lock_mask_in_place;
return LockingMode == LM_LIGHTWEIGHT ? lockbits == monitor_value // monitor?
: (lockbits & unlocked_value) == 0; // monitor | stack-locked?
if (LockingMode == LM_LIGHTWEIGHT) {
return !UseObjectMonitorTable && lockbits == monitor_value;
}
// monitor (0b10) | stack-locked (0b00)?
return (lockbits & unlocked_value) == 0;
}
markWord displaced_mark_helper() const;
void set_displaced_mark_helper(markWord m) const;
@ -223,10 +227,15 @@ class markWord {
return from_pointer(lock);
}
static markWord encode(ObjectMonitor* monitor) {
assert(!UseObjectMonitorTable, "Lightweight locking with OM table does not use markWord for monitors");
uintptr_t tmp = (uintptr_t) monitor;
return markWord(tmp | monitor_value);
}
markWord set_has_monitor() const {
return markWord((value() & ~lock_mask_in_place) | monitor_value);
}
// used to encode pointers during GC
markWord clear_lock_bits() const { return markWord(value() & ~lock_mask_in_place); }

View File

@ -103,6 +103,7 @@ private:
Register _mark;
Register _t;
Register _thread;
Label _slow_path;
Label _push_and_slow_path;
Label _check_successor;
Label _unlocked_continuation;
@ -111,6 +112,7 @@ public:
_obj(obj), _mark(mark), _t(t), _thread(thread) {}
int max_size() const;
void emit(C2_MacroAssembler& masm);
Label& slow_path() { return _slow_path; }
Label& push_and_slow_path() { return _push_and_slow_path; }
Label& check_successor() { return _check_successor; }
Label& unlocked_continuation() { return _unlocked_continuation; }

View File

@ -4604,21 +4604,23 @@ bool LibraryCallKit::inline_native_hashcode(bool is_virtual, bool is_static) {
Node* no_ctrl = nullptr;
Node* header = make_load(no_ctrl, header_addr, TypeX_X, TypeX_X->basic_type(), MemNode::unordered);
// Test the header to see if it is safe to read w.r.t. locking.
Node *lock_mask = _gvn.MakeConX(markWord::lock_mask_in_place);
Node *lmasked_header = _gvn.transform(new AndXNode(header, lock_mask));
if (LockingMode == LM_LIGHTWEIGHT) {
Node *monitor_val = _gvn.MakeConX(markWord::monitor_value);
Node *chk_monitor = _gvn.transform(new CmpXNode(lmasked_header, monitor_val));
Node *test_monitor = _gvn.transform(new BoolNode(chk_monitor, BoolTest::eq));
if (!UseObjectMonitorTable) {
// Test the header to see if it is safe to read w.r.t. locking.
Node *lock_mask = _gvn.MakeConX(markWord::lock_mask_in_place);
Node *lmasked_header = _gvn.transform(new AndXNode(header, lock_mask));
if (LockingMode == LM_LIGHTWEIGHT) {
Node *monitor_val = _gvn.MakeConX(markWord::monitor_value);
Node *chk_monitor = _gvn.transform(new CmpXNode(lmasked_header, monitor_val));
Node *test_monitor = _gvn.transform(new BoolNode(chk_monitor, BoolTest::eq));
generate_slow_guard(test_monitor, slow_region);
} else {
Node *unlocked_val = _gvn.MakeConX(markWord::unlocked_value);
Node *chk_unlocked = _gvn.transform(new CmpXNode(lmasked_header, unlocked_val));
Node *test_not_unlocked = _gvn.transform(new BoolNode(chk_unlocked, BoolTest::ne));
generate_slow_guard(test_monitor, slow_region);
} else {
Node *unlocked_val = _gvn.MakeConX(markWord::unlocked_value);
Node *chk_unlocked = _gvn.transform(new CmpXNode(lmasked_header, unlocked_val));
Node *test_not_unlocked = _gvn.transform(new BoolNode(chk_unlocked, BoolTest::ne));
generate_slow_guard(test_not_unlocked, slow_region);
generate_slow_guard(test_not_unlocked, slow_region);
}
}
// Get the hash value and check to see that it has been properly assigned.

View File

@ -56,6 +56,7 @@
#include "runtime/osThread.hpp"
#include "runtime/signature.hpp"
#include "runtime/stackWatermarkSet.inline.hpp"
#include "runtime/synchronizer.inline.hpp"
#include "runtime/threads.hpp"
#include "runtime/threadSMR.inline.hpp"
#include "runtime/vframe.inline.hpp"
@ -1465,7 +1466,6 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
ThreadsListHandle tlh(current_thread);
JavaThread *owning_thread = nullptr;
ObjectMonitor *mon = nullptr;
jvmtiMonitorUsage ret = {
nullptr, 0, 0, nullptr, 0, nullptr
};
@ -1495,9 +1495,11 @@ JvmtiEnvBase::get_object_monitor_usage(JavaThread* calling_thread, jobject objec
ResourceMark rm(current_thread);
GrowableArray<JavaThread*>* wantList = nullptr;
if (mark.has_monitor()) {
mon = mark.monitor();
assert(mon != nullptr, "must have monitor");
ObjectMonitor* mon = mark.has_monitor()
? ObjectSynchronizer::read_monitor(current_thread, hobj(), mark)
: nullptr;
if (mon != nullptr) {
// this object has a heavyweight monitor
nWant = mon->contentions(); // # of threads contending for monitor entry, but not re-entry
nWait = mon->waiters(); // # of threads waiting for notification,

View File

@ -1816,8 +1816,18 @@ bool Arguments::check_vm_args_consistency() {
FLAG_SET_CMDLINE(LockingMode, LM_LEGACY);
warning("New lightweight locking not supported on this platform");
}
if (UseObjectMonitorTable) {
FLAG_SET_CMDLINE(UseObjectMonitorTable, false);
warning("UseObjectMonitorTable not supported on this platform");
}
#endif
if (UseObjectMonitorTable && LockingMode != LM_LIGHTWEIGHT) {
// ObjectMonitorTable requires lightweight locking.
FLAG_SET_CMDLINE(UseObjectMonitorTable, false);
warning("UseObjectMonitorTable requires LM_LIGHTWEIGHT");
}
#if !defined(X86) && !defined(AARCH64) && !defined(PPC64) && !defined(RISCV64) && !defined(S390)
if (LockingMode == LM_MONITOR) {
jio_fprintf(defaultStream::error_stream(),

View File

@ -24,16 +24,24 @@
#include "precompiled.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/basicLock.hpp"
#include "runtime/basicLock.inline.hpp"
#include "runtime/objectMonitor.hpp"
#include "runtime/synchronizer.hpp"
void BasicLock::print_on(outputStream* st, oop owner) const {
st->print("monitor");
markWord mark_word = displaced_header();
if (mark_word.value() != 0) {
// Print monitor info if there's an owning oop and it refers to this BasicLock.
bool print_monitor_info = (owner != nullptr) && (owner->mark() == markWord::from_pointer((void*)this));
mark_word.print_on(st, print_monitor_info);
if (UseObjectMonitorTable) {
ObjectMonitor* mon = object_monitor_cache();
if (mon != nullptr) {
mon->print_on(st);
}
} else if (LockingMode == LM_LEGACY) {
markWord mark_word = displaced_header();
if (mark_word.value() != 0) {
// Print monitor info if there's an owning oop and it refers to this BasicLock.
bool print_monitor_info = (owner != nullptr) && (owner->mark() == markWord::from_pointer((void*)this));
mark_word.print_on(st, print_monitor_info);
}
}
}
@ -82,10 +90,15 @@ void BasicLock::move_to(oop obj, BasicLock* dest) {
// we can find any flavor mark in the displaced mark.
}
dest->set_displaced_header(displaced_header());
} else if (UseObjectMonitorTable) {
// Preserve the ObjectMonitor*, the cache is cleared when a box is reused
// and only read while the lock is held, so no stale ObjectMonitor* is
// encountered.
dest->set_object_monitor_cache(object_monitor_cache());
}
#ifdef ASSERT
else {
dest->set_displaced_header(markWord(badDispHeaderDeopt));
dest->set_bad_metadata_deopt();
}
#endif
}

View File

@ -28,30 +28,46 @@
#include "oops/markWord.hpp"
#include "runtime/atomic.hpp"
#include "runtime/handles.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/sizes.hpp"
class BasicLock {
friend class VMStructs;
friend class JVMCIVMStructs;
private:
// * For LM_MONITOR
// Unused.
// * For LM_LEGACY
// This is either the actual displaced header from a locked object, or
// a sentinel zero value indicating a recursive stack-lock.
volatile markWord _displaced_header;
public:
markWord displaced_header() const {
return Atomic::load(&_displaced_header);
}
// * For LM_LIGHTWEIGHT
// Used as a cache of the ObjectMonitor* used when locking. Must either
// be nullptr or the ObjectMonitor* used when locking.
volatile uintptr_t _metadata;
void set_displaced_header(markWord header) {
Atomic::store(&_displaced_header, header);
}
uintptr_t get_metadata() const { return Atomic::load(&_metadata); }
void set_metadata(uintptr_t value) { Atomic::store(&_metadata, value); }
static int metadata_offset_in_bytes() { return (int)offset_of(BasicLock, _metadata); }
public:
// LM_MONITOR
void set_bad_metadata_deopt() { set_metadata(badDispHeaderDeopt); }
// LM_LEGACY
inline markWord displaced_header() const;
inline void set_displaced_header(markWord header);
static int displaced_header_offset_in_bytes() { return metadata_offset_in_bytes(); }
// LM_LIGHTWEIGHT
inline ObjectMonitor* object_monitor_cache() const;
inline void clear_object_monitor_cache();
inline void set_object_monitor_cache(ObjectMonitor* mon);
static int object_monitor_cache_offset_in_bytes() { return metadata_offset_in_bytes(); }
void print_on(outputStream* st, oop owner) const;
// move a basic lock (used during deoptimization)
void move_to(oop obj, BasicLock* dest);
static int displaced_header_offset_in_bytes() { return (int)offset_of(BasicLock, _displaced_header); }
};
// A BasicObjectLock associates a specific Java object with a BasicLock.

View File

@ -0,0 +1,62 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_RUNTIME_BASICLOCK_INLINE_HPP
#define SHARE_RUNTIME_BASICLOCK_INLINE_HPP
#include "runtime/basicLock.hpp"
inline markWord BasicLock::displaced_header() const {
assert(LockingMode == LM_LEGACY, "must be");
return markWord(get_metadata());
}
inline void BasicLock::set_displaced_header(markWord header) {
assert(LockingMode == LM_LEGACY, "must be");
Atomic::store(&_metadata, header.value());
}
inline ObjectMonitor* BasicLock::object_monitor_cache() const {
assert(UseObjectMonitorTable, "must be");
#if defined(X86) || defined(AARCH64)
return reinterpret_cast<ObjectMonitor*>(get_metadata());
#else
// Other platforms do not make use of the cache yet,
// and are not as careful with maintaining the invariant
// that the metadata either is nullptr or ObjectMonitor*.
return nullptr;
#endif
}
inline void BasicLock::clear_object_monitor_cache() {
assert(UseObjectMonitorTable, "must be");
set_metadata(0);
}
inline void BasicLock::set_object_monitor_cache(ObjectMonitor* mon) {
assert(UseObjectMonitorTable, "must be");
set_metadata(reinterpret_cast<uintptr_t>(mon));
}
#endif // SHARE_RUNTIME_BASICLOCK_INLINE_HPP

View File

@ -63,6 +63,7 @@
#include "prims/methodHandles.hpp"
#include "prims/vectorSupport.hpp"
#include "runtime/atomic.hpp"
#include "runtime/basicLock.inline.hpp"
#include "runtime/continuation.hpp"
#include "runtime/continuationEntry.inline.hpp"
#include "runtime/deoptimization.hpp"
@ -75,6 +76,8 @@
#include "runtime/javaThread.hpp"
#include "runtime/jniHandles.inline.hpp"
#include "runtime/keepStackGCProcessed.hpp"
#include "runtime/lightweightSynchronizer.hpp"
#include "runtime/lockStack.inline.hpp"
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/osThread.hpp"
#include "runtime/safepointVerifiers.hpp"
@ -84,7 +87,7 @@
#include "runtime/stackValue.hpp"
#include "runtime/stackWatermarkSet.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/synchronizer.inline.hpp"
#include "runtime/threadSMR.hpp"
#include "runtime/threadWXSetters.inline.hpp"
#include "runtime/vframe.hpp"
@ -1634,22 +1637,37 @@ bool Deoptimization::relock_objects(JavaThread* thread, GrowableArray<MonitorInf
ObjectMonitor* waiting_monitor = deoptee_thread->current_waiting_monitor();
if (waiting_monitor != nullptr && waiting_monitor->object() == obj()) {
assert(fr.is_deoptimized_frame(), "frame must be scheduled for deoptimization");
mon_info->lock()->set_displaced_header(markWord::unused_mark());
if (LockingMode == LM_LEGACY) {
mon_info->lock()->set_displaced_header(markWord::unused_mark());
} else if (UseObjectMonitorTable) {
mon_info->lock()->clear_object_monitor_cache();
}
#ifdef ASSERT
else {
assert(LockingMode == LM_MONITOR || !UseObjectMonitorTable, "must be");
mon_info->lock()->set_bad_metadata_deopt();
}
#endif
JvmtiDeferredUpdates::inc_relock_count_after_wait(deoptee_thread);
continue;
}
}
}
BasicLock* lock = mon_info->lock();
if (LockingMode == LM_LIGHTWEIGHT) {
// We have lost information about the correct state of the lock stack.
// Inflate the locks instead. Enter then inflate to avoid races with
// deflation.
ObjectSynchronizer::enter_for(obj, nullptr, deoptee_thread);
// Entering may create an invalid lock stack. Inflate the lock if it
// was fast_locked to restore the valid lock stack.
ObjectSynchronizer::enter_for(obj, lock, deoptee_thread);
if (deoptee_thread->lock_stack().contains(obj())) {
LightweightSynchronizer::inflate_fast_locked_object(obj(), ObjectSynchronizer::InflateCause::inflate_cause_vm_internal,
deoptee_thread, thread);
}
assert(mon_info->owner()->is_locked(), "object must be locked now");
ObjectMonitor* mon = ObjectSynchronizer::inflate_for(deoptee_thread, obj(), ObjectSynchronizer::inflate_cause_vm_internal);
assert(mon->owner() == deoptee_thread, "must be");
assert(obj->mark().has_monitor(), "must be");
assert(!deoptee_thread->lock_stack().contains(obj()), "must be");
assert(ObjectSynchronizer::read_monitor(thread, obj(), obj->mark())->owner() == deoptee_thread, "must be");
} else {
BasicLock* lock = mon_info->lock();
ObjectSynchronizer::enter_for(obj, lock, deoptee_thread);
assert(mon_info->owner()->is_locked(), "object must be locked now");
}

View File

@ -1956,6 +1956,17 @@ const int ObjectAlignmentInBytes = 8;
"2: monitors & new lightweight locking (LM_LIGHTWEIGHT, default)") \
range(0, 2) \
\
product(bool, UseObjectMonitorTable, false, DIAGNOSTIC, \
"With Lightweight Locking mode, use a table to record inflated " \
"monitors rather than the first word of the object.") \
\
product(int, LightweightFastLockingSpins, 13, DIAGNOSTIC, \
"Specifies the number of times lightweight fast locking will " \
"attempt to CAS the markWord before inflating. Between each " \
"CAS it will spin for exponentially more time, resulting in " \
"a total number of spins on the order of O(2^value)") \
range(1, 30) \
\
product(uint, TrimNativeHeapInterval, 0, \
"Interval, in ms, at which the JVM will trim the native heap if " \
"the platform supports that. Lower values will reclaim memory " \

View File

@ -504,7 +504,8 @@ JavaThread::JavaThread(MEMFLAGS flags) :
_SleepEvent(ParkEvent::Allocate(this)),
_lock_stack(this) {
_lock_stack(this),
_om_cache(this) {
set_jni_functions(jni_functions());
#if INCLUDE_JVMCI
@ -803,6 +804,8 @@ void JavaThread::exit(bool destroy_vm, ExitType exit_type) {
elapsedTimer _timer_exit_phase3;
elapsedTimer _timer_exit_phase4;
om_clear_monitor_cache();
if (log_is_enabled(Debug, os, thread, timer)) {
_timer_exit_phase1.start();
}

View File

@ -61,6 +61,7 @@ class JvmtiSampledObjectAllocEventCollector;
class JvmtiThreadState;
class Metadata;
class ObjectMonitor;
class OopHandleList;
class OopStorage;
class OSThread;
@ -1165,6 +1166,7 @@ public:
private:
LockStack _lock_stack;
OMCache _om_cache;
public:
LockStack& lock_stack() { return _lock_stack; }
@ -1176,6 +1178,13 @@ public:
static ByteSize lock_stack_top_offset() { return lock_stack_offset() + LockStack::top_offset(); }
static ByteSize lock_stack_base_offset() { return lock_stack_offset() + LockStack::base_offset(); }
static ByteSize om_cache_offset() { return byte_offset_of(JavaThread, _om_cache); }
static ByteSize om_cache_oops_offset() { return om_cache_offset() + OMCache::entries_offset(); }
void om_set_monitor_cache(ObjectMonitor* monitor);
void om_clear_monitor_cache();
ObjectMonitor* om_get_from_monitor_cache(oop obj);
static OopStorage* thread_oop_storage();
static void verify_cross_modify_fence_failure(JavaThread *thread) PRODUCT_RETURN;

View File

@ -36,7 +36,9 @@
#include "runtime/atomic.hpp"
#include "runtime/continuation.hpp"
#include "runtime/continuationEntry.inline.hpp"
#include "runtime/lockStack.inline.hpp"
#include "runtime/nonJavaThread.hpp"
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/orderAccess.hpp"
#include "runtime/safepoint.hpp"
@ -239,4 +241,25 @@ inline InstanceKlass* JavaThread::class_to_be_initialized() const {
return _class_to_be_initialized;
}
inline void JavaThread::om_set_monitor_cache(ObjectMonitor* monitor) {
assert(UseObjectMonitorTable, "must be");
assert(monitor != nullptr, "use om_clear_monitor_cache to clear");
assert(this == current() || monitor->owner_raw() == this, "only add owned monitors for other threads");
assert(this == current() || is_obj_deopt_suspend(), "thread must not run concurrently");
_om_cache.set_monitor(monitor);
}
inline void JavaThread::om_clear_monitor_cache() {
if (UseObjectMonitorTable) {
_om_cache.clear();
}
}
inline ObjectMonitor* JavaThread::om_get_from_monitor_cache(oop obj) {
assert(obj != nullptr, "do not look for null objects");
assert(this == current(), "only get own thread locals");
return _om_cache.get_monitor(obj);
}
#endif // SHARE_RUNTIME_JAVATHREAD_INLINE_HPP

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,80 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_RUNTIME_LIGHTWEIGHTSYNCHRONIZER_HPP
#define SHARE_RUNTIME_LIGHTWEIGHTSYNCHRONIZER_HPP
#include "memory/allStatic.hpp"
#include "runtime/javaThread.hpp"
#include "runtime/objectMonitor.hpp"
#include "runtime/synchronizer.hpp"
class ObjectMonitorTable;
class LightweightSynchronizer : AllStatic {
private:
static ObjectMonitor* get_or_insert_monitor_from_table(oop object, JavaThread* current, bool* inserted);
static ObjectMonitor* get_or_insert_monitor(oop object, JavaThread* current, ObjectSynchronizer::InflateCause cause);
static ObjectMonitor* add_monitor(JavaThread* current, ObjectMonitor* monitor, oop obj);
static bool remove_monitor(Thread* current, ObjectMonitor* monitor, oop obj);
static void deflate_mark_word(oop object);
static void ensure_lock_stack_space(JavaThread* current);
class CacheSetter;
class LockStackInflateContendedLocks;
class VerifyThreadState;
public:
static void initialize();
static bool needs_resize();
static bool resize_table(JavaThread* current);
private:
static inline bool fast_lock_try_enter(oop obj, LockStack& lock_stack, JavaThread* current);
static bool fast_lock_spin_enter(oop obj, LockStack& lock_stack, JavaThread* current, bool observed_deflation);
public:
static void enter_for(Handle obj, BasicLock* lock, JavaThread* locking_thread);
static void enter(Handle obj, BasicLock* lock, JavaThread* current);
static void exit(oop object, JavaThread* current);
static ObjectMonitor* inflate_into_object_header(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* inflating_thread, Thread* current);
static ObjectMonitor* inflate_locked_or_imse(oop object, ObjectSynchronizer::InflateCause cause, TRAPS);
static ObjectMonitor* inflate_fast_locked_object(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current);
static ObjectMonitor* inflate_and_enter(oop object, ObjectSynchronizer::InflateCause cause, JavaThread* locking_thread, JavaThread* current);
static void deflate_monitor(Thread* current, oop obj, ObjectMonitor* monitor);
static ObjectMonitor* get_monitor_from_table(Thread* current, oop obj);
static bool contains_monitor(Thread* current, ObjectMonitor* monitor);
static bool quick_enter(oop obj, BasicLock* Lock, JavaThread* current);
};
#endif // SHARE_RUNTIME_LIGHTWEIGHTSYNCHRONIZER_HPP

View File

@ -29,17 +29,20 @@
#include "oops/markWord.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/globals.hpp"
#include "runtime/javaThread.inline.hpp"
#include "runtime/lockStack.inline.hpp"
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/stackWatermark.hpp"
#include "runtime/stackWatermarkSet.inline.hpp"
#include "runtime/synchronizer.inline.hpp"
#include "runtime/thread.hpp"
#include "utilities/copy.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/growableArray.hpp"
#include "utilities/ostream.hpp"
#include "utilities/sizes.hpp"
#include <type_traits>
@ -114,3 +117,11 @@ void LockStack::print_on(outputStream* st) {
}
}
}
OMCache::OMCache(JavaThread* jt) : _entries() {
STATIC_ASSERT(std::is_standard_layout<OMCache>::value);
STATIC_ASSERT(std::is_standard_layout<OMCache::OMCacheEntry>::value);
STATIC_ASSERT(offsetof(OMCache, _null_sentinel) == offsetof(OMCache, _entries) +
offsetof(OMCache::OMCacheEntry, _oop) +
OMCache::CAPACITY * in_bytes(oop_to_oop_difference()));
}

View File

@ -32,18 +32,20 @@
#include "utilities/sizes.hpp"
class JavaThread;
class ObjectMonitor;
class OopClosure;
class outputStream;
template<typename>
class GrowableArray;
class Thread;
class LockStack {
friend class LockStackTest;
friend class VMStructs;
JVMCI_ONLY(friend class JVMCIVMStructs;)
public:
public:
static const int CAPACITY = 8;
private:
private:
// TODO: It would be very useful if JavaThread::lock_stack_offset() and friends were constexpr,
// but this is currently not the case because we're using offset_of() which is non-constexpr,
@ -73,7 +75,7 @@ private:
// Given an offset (in bytes) calculate the index into the lock-stack.
static inline int to_index(uint32_t offset);
public:
public:
static ByteSize top_offset() { return byte_offset_of(LockStack, _top); }
static ByteSize base_offset() { return byte_offset_of(LockStack, _base); }
@ -123,4 +125,29 @@ public:
void print_on(outputStream* st);
};
class OMCache {
friend class VMStructs;
public:
static constexpr int CAPACITY = 8;
private:
struct OMCacheEntry {
oop _oop = nullptr;
ObjectMonitor* _monitor = nullptr;
} _entries[CAPACITY];
const oop _null_sentinel = nullptr;
public:
static ByteSize entries_offset() { return byte_offset_of(OMCache, _entries); }
static constexpr ByteSize oop_to_oop_difference() { return in_ByteSize(sizeof(OMCacheEntry)); }
static constexpr ByteSize oop_to_monitor_difference() { return in_ByteSize(sizeof(oop)); }
explicit OMCache(JavaThread* jt);
inline ObjectMonitor* get_monitor(oop o);
inline void set_monitor(ObjectMonitor* monitor);
inline void clear();
};
#endif // SHARE_RUNTIME_LOCKSTACK_HPP

View File

@ -31,6 +31,8 @@
#include "memory/iterator.hpp"
#include "runtime/javaThread.hpp"
#include "runtime/lightweightSynchronizer.hpp"
#include "runtime/objectMonitor.inline.hpp"
#include "runtime/safepoint.hpp"
#include "runtime/stackWatermark.hpp"
#include "runtime/stackWatermarkSet.inline.hpp"
@ -222,4 +224,54 @@ inline void LockStack::oops_do(OopClosure* cl) {
verify("post-oops-do");
}
inline void OMCache::set_monitor(ObjectMonitor *monitor) {
const int end = OMCache::CAPACITY - 1;
oop obj = monitor->object_peek();
assert(obj != nullptr, "must be alive");
assert(monitor == LightweightSynchronizer::get_monitor_from_table(JavaThread::current(), obj), "must exist in table");
OMCacheEntry to_insert = {obj, monitor};
for (int i = 0; i < end; ++i) {
if (_entries[i]._oop == obj ||
_entries[i]._monitor == nullptr ||
_entries[i]._monitor->is_being_async_deflated()) {
// Use stale slot.
_entries[i] = to_insert;
return;
}
// Swap with the most recent value.
::swap(to_insert, _entries[i]);
}
_entries[end] = to_insert;
}
inline ObjectMonitor* OMCache::get_monitor(oop o) {
for (int i = 0; i < CAPACITY; ++i) {
if (_entries[i]._oop == o) {
assert(_entries[i]._monitor != nullptr, "monitor must exist");
if (_entries[i]._monitor->is_being_async_deflated()) {
// Bad monitor
// Shift down rest
for (; i < CAPACITY - 1; ++i) {
_entries[i] = _entries[i + 1];
}
// Clear end
_entries[i] = {};
return nullptr;
}
return _entries[i]._monitor;
}
}
return nullptr;
}
inline void OMCache::clear() {
for (size_t i = 0; i < CAPACITY; ++i) {
// Clear
_entries[i] = {};
}
}
#endif // SHARE_RUNTIME_LOCKSTACK_INLINE_HPP

View File

@ -43,6 +43,7 @@
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/javaThread.inline.hpp"
#include "runtime/lightweightSynchronizer.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/objectMonitor.hpp"
#include "runtime/objectMonitor.inline.hpp"
@ -53,6 +54,7 @@
#include "runtime/safepointMechanism.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "services/threadService.hpp"
#include "utilities/debug.hpp"
#include "utilities/dtrace.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/macros.hpp"
@ -246,7 +248,7 @@ static void check_object_context() {
}
ObjectMonitor::ObjectMonitor(oop object) :
_header(markWord::zero()),
_metadata(0),
_object(_oop_storage, object),
_owner(nullptr),
_previous_owner_tid(0),
@ -272,10 +274,6 @@ oop ObjectMonitor::object() const {
return _object.resolve();
}
oop ObjectMonitor::object_peek() const {
return _object.peek();
}
void ObjectMonitor::ExitOnSuspend::operator()(JavaThread* current) {
if (current->is_suspended()) {
_om->_recursions = 0;
@ -297,109 +295,147 @@ void ObjectMonitor::ClearSuccOnSuspend::operator()(JavaThread* current) {
}
}
#define assert_mark_word_consistency() \
assert(UseObjectMonitorTable || object()->mark() == markWord::encode(this), \
"object mark must match encoded this: mark=" INTPTR_FORMAT \
", encoded this=" INTPTR_FORMAT, object()->mark().value(), \
markWord::encode(this).value());
// -----------------------------------------------------------------------------
// Enter support
bool ObjectMonitor::enter_for(JavaThread* locking_thread) {
bool ObjectMonitor::enter_is_async_deflating() {
if (is_being_async_deflated()) {
if (!UseObjectMonitorTable) {
const oop l_object = object();
if (l_object != nullptr) {
// Attempt to restore the header/dmw to the object's header so that
// we only retry once if the deflater thread happens to be slow.
install_displaced_markword_in_object(l_object);
}
}
return true;
}
return false;
}
void ObjectMonitor::enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark) {
// Used by ObjectSynchronizer::enter_for to enter for another thread.
// The monitor is private to or already owned by locking_thread which must be suspended.
// So this code may only contend with deflation.
assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be");
assert(contention_mark._monitor == this, "must be");
assert(!is_being_async_deflated(), "must be");
// Block out deflation as soon as possible.
add_to_contentions(1);
void* prev_owner = try_set_owner_from(nullptr, locking_thread);
bool success = false;
if (!is_being_async_deflated()) {
void* prev_owner = try_set_owner_from(nullptr, locking_thread);
if (prev_owner == nullptr) {
assert(_recursions == 0, "invariant");
success = true;
} else if (prev_owner == locking_thread) {
_recursions++;
success = true;
} else if (prev_owner == DEFLATER_MARKER) {
// Racing with deflation.
prev_owner = try_set_owner_from(DEFLATER_MARKER, locking_thread);
if (prev_owner == DEFLATER_MARKER) {
// Cancelled deflation. Increment contentions as part of the deflation protocol.
add_to_contentions(1);
success = true;
} else if (prev_owner == nullptr) {
// At this point we cannot race with deflation as we have both incremented
// contentions, seen contention > 0 and seen a DEFLATER_MARKER.
// success will only be false if this races with something other than
// deflation.
prev_owner = try_set_owner_from(nullptr, locking_thread);
success = prev_owner == nullptr;
}
} else if (LockingMode == LM_LEGACY && locking_thread->is_lock_owned((address)prev_owner)) {
assert(_recursions == 0, "must be");
_recursions = 1;
set_owner_from_BasicLock(prev_owner, locking_thread);
if (prev_owner == nullptr) {
assert(_recursions == 0, "invariant");
success = true;
} else if (prev_owner == locking_thread) {
_recursions++;
success = true;
} else if (prev_owner == DEFLATER_MARKER) {
// Racing with deflation.
prev_owner = try_set_owner_from(DEFLATER_MARKER, locking_thread);
if (prev_owner == DEFLATER_MARKER) {
// Cancelled deflation. Increment contentions as part of the deflation protocol.
add_to_contentions(1);
success = true;
} else if (prev_owner == nullptr) {
// At this point we cannot race with deflation as we have both incremented
// contentions, seen contention > 0 and seen a DEFLATER_MARKER.
// success will only be false if this races with something other than
// deflation.
prev_owner = try_set_owner_from(nullptr, locking_thread);
success = prev_owner == nullptr;
}
assert(success, "Failed to enter_for: locking_thread=" INTPTR_FORMAT
", this=" INTPTR_FORMAT "{owner=" INTPTR_FORMAT "}, observed owner: " INTPTR_FORMAT,
p2i(locking_thread), p2i(this), p2i(owner_raw()), p2i(prev_owner));
} else {
// Async deflation is in progress and our contentions increment
// above lost the race to async deflation. Undo the work and
// force the caller to retry.
const oop l_object = object();
if (l_object != nullptr) {
// Attempt to restore the header/dmw to the object's header so that
// we only retry once if the deflater thread happens to be slow.
install_displaced_markword_in_object(l_object);
}
} else if (LockingMode == LM_LEGACY && locking_thread->is_lock_owned((address)prev_owner)) {
assert(_recursions == 0, "must be");
_recursions = 1;
set_owner_from_BasicLock(prev_owner, locking_thread);
success = true;
}
add_to_contentions(-1);
assert(!success || owner_raw() == locking_thread, "must be");
return success;
assert(success, "Failed to enter_for: locking_thread=" INTPTR_FORMAT
", this=" INTPTR_FORMAT "{owner=" INTPTR_FORMAT "}, observed owner: " INTPTR_FORMAT,
p2i(locking_thread), p2i(this), p2i(owner_raw()), p2i(prev_owner));
}
bool ObjectMonitor::enter(JavaThread* current) {
assert(current == JavaThread::current(), "must be");
// The following code is ordered to check the most common cases first
// and to reduce RTS->RTO cache line upgrades on SPARC and IA32 processors.
bool ObjectMonitor::enter_for(JavaThread* locking_thread) {
void* cur = try_set_owner_from(nullptr, current);
if (cur == nullptr) {
// Block out deflation as soon as possible.
ObjectMonitorContentionMark contention_mark(this);
// Check for deflation.
if (enter_is_async_deflating()) {
return false;
}
enter_for_with_contention_mark(locking_thread, contention_mark);
assert(owner_raw() == locking_thread, "must be");
return true;
}
bool ObjectMonitor::try_enter(JavaThread* current) {
// TryLock avoids the CAS
TryLockResult r = TryLock(current);
if (r == TryLockResult::Success) {
assert(_recursions == 0, "invariant");
return true;
}
if (cur == current) {
// TODO-FIXME: check for integer overflow! BUGID 6557169.
if (r == TryLockResult::HasOwner && owner() == current) {
_recursions++;
return true;
}
if (LockingMode != LM_LIGHTWEIGHT && current->is_lock_owned((address)cur)) {
void* cur = owner_raw();
if (LockingMode == LM_LEGACY && current->is_lock_owned((address)cur)) {
assert(_recursions == 0, "internal state error");
_recursions = 1;
set_owner_from_BasicLock(cur, current); // Convert from BasicLock* to Thread*.
return true;
}
return false;
}
bool ObjectMonitor::spin_enter(JavaThread* current) {
assert(current == JavaThread::current(), "must be");
// Check for recursion.
if (try_enter(current)) {
return true;
}
// Check for deflation.
if (enter_is_async_deflating()) {
return false;
}
// We've encountered genuine contention.
// Try one round of spinning *before* enqueueing current
// and before going through the awkward and expensive state
// transitions. The following spin is strictly optional ...
// Do one round of spinning.
// Note that if we acquire the monitor from an initial spin
// we forgo posting JVMTI events and firing DTRACE probes.
if (TrySpin(current)) {
assert(owner_raw() == current, "must be current: owner=" INTPTR_FORMAT, p2i(owner_raw()));
assert(_recursions == 0, "must be 0: recursions=" INTX_FORMAT, _recursions);
assert(object()->mark() == markWord::encode(this),
"object mark must match encoded this: mark=" INTPTR_FORMAT
", encoded this=" INTPTR_FORMAT, object()->mark().value(),
markWord::encode(this).value());
assert_mark_word_consistency();
return true;
}
return false;
}
bool ObjectMonitor::enter(JavaThread* current) {
assert(current == JavaThread::current(), "must be");
if (spin_enter(current)) {
return true;
}
@ -408,22 +444,25 @@ bool ObjectMonitor::enter(JavaThread* current) {
assert(!SafepointSynchronize::is_at_safepoint(), "invariant");
assert(current->thread_state() != _thread_blocked, "invariant");
// Keep track of contention for JVM/TI and M&M queries.
add_to_contentions(1);
if (is_being_async_deflated()) {
// Async deflation is in progress and our contentions increment
// above lost the race to async deflation. Undo the work and
// force the caller to retry.
const oop l_object = object();
if (l_object != nullptr) {
// Attempt to restore the header/dmw to the object's header so that
// we only retry once if the deflater thread happens to be slow.
install_displaced_markword_in_object(l_object);
}
add_to_contentions(-1);
// Keep is_being_async_deflated stable across the rest of enter
ObjectMonitorContentionMark contention_mark(this);
// Check for deflation.
if (enter_is_async_deflating()) {
return false;
}
// At this point this ObjectMonitor cannot be deflated, finish contended enter
enter_with_contention_mark(current, contention_mark);
return true;
}
void ObjectMonitor::enter_with_contention_mark(JavaThread *current, ObjectMonitorContentionMark &cm) {
assert(current == JavaThread::current(), "must be");
assert(owner_raw() != current, "must be");
assert(cm._monitor == this, "must be");
assert(!is_being_async_deflated(), "must be");
JFR_ONLY(JfrConditionalFlush<EventJavaMonitorEnter> flush(current);)
EventJavaMonitorEnter event;
if (event.is_started()) {
@ -480,14 +519,13 @@ bool ObjectMonitor::enter(JavaThread* current) {
// the monitor free and clear.
}
add_to_contentions(-1);
assert(contentions() >= 0, "must not be negative: contentions=%d", contentions());
// Must either set _recursions = 0 or ASSERT _recursions == 0.
assert(_recursions == 0, "invariant");
assert(owner_raw() == current, "invariant");
assert(_succ != current, "invariant");
assert(object()->mark() == markWord::encode(this), "invariant");
assert_mark_word_consistency();
// The thread -- now the owner -- is back in vm mode.
// Report the glorious news via TI,DTrace and jvmstat.
@ -516,7 +554,6 @@ bool ObjectMonitor::enter(JavaThread* current) {
event.commit();
}
OM_PERFDATA_OP(ContendedLockAttempts, inc());
return true;
}
// Caveat: TryLock() is not necessarily serializing if it returns failure.
@ -549,7 +586,7 @@ ObjectMonitor::TryLockResult ObjectMonitor::TryLock(JavaThread* current) {
// (contentions < 0)
// Contending threads that see that condition know to retry their operation.
//
bool ObjectMonitor::deflate_monitor() {
bool ObjectMonitor::deflate_monitor(Thread* current) {
if (is_busy()) {
// Easy checks are first - the ObjectMonitor is busy so no deflation.
return false;
@ -620,7 +657,11 @@ bool ObjectMonitor::deflate_monitor() {
p2i(obj), obj->mark().value(),
obj->klass()->external_name());
}
}
if (UseObjectMonitorTable) {
LightweightSynchronizer::deflate_monitor(current, obj, this);
} else if (obj != nullptr) {
// Install the old mark word if nobody else has already done it.
install_displaced_markword_in_object(obj);
}
@ -636,6 +677,7 @@ bool ObjectMonitor::deflate_monitor() {
// monitor and by other threads that have detected a race with the
// deflation process.
void ObjectMonitor::install_displaced_markword_in_object(const oop obj) {
assert(!UseObjectMonitorTable, "ObjectMonitorTable has no dmw");
// This function must only be called when (owner == DEFLATER_MARKER
// && contentions <= 0), but we can't guarantee that here because
// those values could change when the ObjectMonitor gets moved from
@ -972,12 +1014,11 @@ void ObjectMonitor::EnterI(JavaThread* current) {
void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) {
assert(current != nullptr, "invariant");
assert(current->thread_state() != _thread_blocked, "invariant");
assert(currentNode != nullptr, "invariant");
assert(currentNode->_thread == current, "invariant");
assert(_waiters > 0, "invariant");
assert(object()->mark() == markWord::encode(this), "invariant");
assert(current->thread_state() != _thread_blocked, "invariant");
assert_mark_word_consistency();
for (;;) {
ObjectWaiter::TStates v = currentNode->TState;
@ -1042,7 +1083,7 @@ void ObjectMonitor::ReenterI(JavaThread* current, ObjectWaiter* currentNode) {
// In addition, current.TState is stable.
assert(owner_raw() == current, "invariant");
assert(object()->mark() == markWord::encode(this), "invariant");
assert_mark_word_consistency();
UnlinkAfterAcquire(current, currentNode);
if (_succ == current) _succ = nullptr;
assert(_succ != current, "invariant");
@ -1668,7 +1709,7 @@ void ObjectMonitor::wait(jlong millis, bool interruptible, TRAPS) {
// Verify a few postconditions
assert(owner_raw() == current, "invariant");
assert(_succ != current, "invariant");
assert(object()->mark() == markWord::encode(this), "invariant");
assert_mark_word_consistency();
// check if the notification happened
if (!WasNotified) {
@ -2185,7 +2226,7 @@ void ObjectMonitor::print() const { print_on(tty); }
// Print the ObjectMonitor like a debugger would:
//
// (ObjectMonitor) 0x00007fdfb6012e40 = {
// _header = 0x0000000000000001
// _metadata = 0x0000000000000001
// _object = 0x000000070ff45fd0
// _pad_buf0 = {
// [0] = '\0'
@ -2214,7 +2255,7 @@ void ObjectMonitor::print() const { print_on(tty); }
//
void ObjectMonitor::print_debug_style_on(outputStream* st) const {
st->print_cr("(ObjectMonitor*) " INTPTR_FORMAT " = {", p2i(this));
st->print_cr(" _header = " INTPTR_FORMAT, header().value());
st->print_cr(" _metadata = " INTPTR_FORMAT, _metadata);
st->print_cr(" _object = " INTPTR_FORMAT, p2i(object_peek()));
st->print_cr(" _pad_buf0 = {");
st->print_cr(" [0] = '\\0'");

View File

@ -33,6 +33,7 @@
#include "utilities/checkedCast.hpp"
class ObjectMonitor;
class ObjectMonitorContentionMark;
class ParkEvent;
// ObjectWaiter serves as a "proxy" or surrogate thread.
@ -69,20 +70,21 @@ class ObjectWaiter : public StackObj {
//
// ObjectMonitor Layout Overview/Highlights/Restrictions:
//
// - The _header field must be at offset 0 because the displaced header
// - The _metadata field must be at offset 0 because the displaced header
// from markWord is stored there. We do not want markWord.hpp to include
// ObjectMonitor.hpp to avoid exposing ObjectMonitor everywhere. This
// means that ObjectMonitor cannot inherit from any other class nor can
// it use any virtual member functions. This restriction is critical to
// the proper functioning of the VM.
// - The _header and _owner fields should be separated by enough space
// - The _metadata and _owner fields should be separated by enough space
// to avoid false sharing due to parallel access by different threads.
// This is an advisory recommendation.
// - The general layout of the fields in ObjectMonitor is:
// _header
// _metadata
// <lightly_used_fields>
// <optional padding>
// _owner
// <optional padding>
// <remaining_fields>
// - The VM assumes write ordering and machine word alignment with
// respect to the _owner field and the <remaining_fields> that can
@ -106,20 +108,19 @@ class ObjectWaiter : public StackObj {
// in synchronizer.cpp. Also see TEST_VM(SynchronizerTest, sanity) gtest.
//
// Futures notes:
// - Separating _owner from the <remaining_fields> by enough space to
// avoid false sharing might be profitable. Given
// http://blogs.oracle.com/dave/entry/cas_and_cache_trivia_invalidate
// we know that the CAS in monitorenter will invalidate the line
// underlying _owner. We want to avoid an L1 data cache miss on that
// same line for monitorexit. Putting these <remaining_fields>:
// _recursions, _EntryList, _cxq, and _succ, all of which may be
// fetched in the inflated unlock path, on a different cache line
// would make them immune to CAS-based invalidation from the _owner
// field.
// - Separating _owner from the <remaining_fields> by enough space to
// avoid false sharing might be profitable. Given that the CAS in
// monitorenter will invalidate the line underlying _owner. We want
// to avoid an L1 data cache miss on that same line for monitorexit.
// Putting these <remaining_fields>:
// _recursions, _EntryList, _cxq, and _succ, all of which may be
// fetched in the inflated unlock path, on a different cache line
// would make them immune to CAS-based invalidation from the _owner
// field.
//
// - The _recursions field should be of type int, or int32_t but not
// intptr_t. There's no reason to use a 64-bit type for this field
// in a 64-bit JVM.
// - The _recursions field should be of type int, or int32_t but not
// intptr_t. There's no reason to use a 64-bit type for this field
// in a 64-bit JVM.
#define OM_CACHE_LINE_SIZE DEFAULT_CACHE_LINE_SIZE
@ -131,15 +132,19 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
static OopStorage* _oop_storage;
// The sync code expects the header field to be at offset zero (0).
// Enforced by the assert() in header_addr().
volatile markWord _header; // displaced object header word - mark
// The sync code expects the metadata field to be at offset zero (0).
// Enforced by the assert() in metadata_addr().
// * LM_LIGHTWEIGHT with UseObjectMonitorTable:
// Contains the _object's hashCode.
// * LM_LEGACY, LM_MONITOR, LM_LIGHTWEIGHT without UseObjectMonitorTable:
// Contains the displaced object header word - mark
volatile uintptr_t _metadata; // metadata
WeakHandle _object; // backward object pointer
// Separate _header and _owner on different cache lines since both can
// have busy multi-threaded access. _header and _object are set at initial
// Separate _metadata and _owner on different cache lines since both can
// have busy multi-threaded access. _metadata and _object are set at initial
// inflation. The _object does not change, so it is a good choice to share
// its cache line with _header.
DEFINE_PAD_MINUS_SIZE(0, OM_CACHE_LINE_SIZE, sizeof(volatile markWord) +
// its cache line with _metadata.
DEFINE_PAD_MINUS_SIZE(0, OM_CACHE_LINE_SIZE, sizeof(_metadata) +
sizeof(WeakHandle));
// Used by async deflation as a marker in the _owner field.
// Note that the choice of the two markers is peculiar:
@ -149,12 +154,13 @@ class ObjectMonitor : public CHeapObj<mtObjectMonitor> {
// and small values encode much better.
// - We test for anonymous owner by testing for the lowest bit, therefore
// DEFLATER_MARKER must *not* have that bit set.
#define DEFLATER_MARKER reinterpret_cast<void*>(2)
public:
static const uintptr_t DEFLATER_MARKER_VALUE = 2;
#define DEFLATER_MARKER reinterpret_cast<void*>(DEFLATER_MARKER_VALUE)
public:
// NOTE: Typed as uintptr_t so that we can pick it up in SA, via vmStructs.
static const uintptr_t ANONYMOUS_OWNER = 1;
private:
private:
static void* anon_owner_ptr() { return reinterpret_cast<void*>(ANONYMOUS_OWNER); }
void* volatile _owner; // pointer to owning thread OR BasicLock
@ -181,10 +187,9 @@ private:
// along with other fields to determine if an ObjectMonitor can be
// deflated. It is also used by the async deflation protocol. See
// ObjectMonitor::deflate_monitor().
protected:
ObjectWaiter* volatile _WaitSet; // LL of threads wait()ing on the monitor
volatile int _waiters; // number of waiting threads
private:
volatile int _WaitSetLock; // protects Wait Queue - simple spinlock
public:
@ -213,6 +218,7 @@ private:
static int Knob_SpinLimit;
static ByteSize metadata_offset() { return byte_offset_of(ObjectMonitor, _metadata); }
static ByteSize owner_offset() { return byte_offset_of(ObjectMonitor, _owner); }
static ByteSize recursions_offset() { return byte_offset_of(ObjectMonitor, _recursions); }
static ByteSize cxq_offset() { return byte_offset_of(ObjectMonitor, _cxq); }
@ -233,9 +239,15 @@ private:
#define OM_OFFSET_NO_MONITOR_VALUE_TAG(f) \
((in_bytes(ObjectMonitor::f ## _offset())) - checked_cast<int>(markWord::monitor_value))
markWord header() const;
volatile markWord* header_addr();
void set_header(markWord hdr);
uintptr_t metadata() const;
void set_metadata(uintptr_t value);
volatile uintptr_t* metadata_addr();
markWord header() const;
void set_header(markWord hdr);
intptr_t hash() const;
void set_hash(intptr_t hash);
bool is_busy() const {
// TODO-FIXME: assert _owner == null implies _recursions = 0
@ -306,6 +318,8 @@ private:
oop object() const;
oop object_peek() const;
bool object_is_dead() const;
bool object_refers_to(oop obj) const;
// Returns true if the specified thread owns the ObjectMonitor. Otherwise
// returns false and throws IllegalMonitorStateException (IMSE).
@ -328,9 +342,15 @@ private:
ClearSuccOnSuspend(ObjectMonitor* om) : _om(om) {}
void operator()(JavaThread* current);
};
bool enter_is_async_deflating();
public:
void enter_for_with_contention_mark(JavaThread* locking_thread, ObjectMonitorContentionMark& contention_mark);
bool enter_for(JavaThread* locking_thread);
bool enter(JavaThread* current);
bool try_enter(JavaThread* current);
bool spin_enter(JavaThread* current);
void enter_with_contention_mark(JavaThread* current, ObjectMonitorContentionMark& contention_mark);
void exit(JavaThread* current, bool not_suspended = true);
void wait(jlong millis, bool interruptible, TRAPS);
void notify(TRAPS);
@ -364,8 +384,23 @@ private:
void ExitEpilog(JavaThread* current, ObjectWaiter* Wakee);
// Deflation support
bool deflate_monitor();
bool deflate_monitor(Thread* current);
private:
void install_displaced_markword_in_object(const oop obj);
};
// RAII object to ensure that ObjectMonitor::is_being_async_deflated() is
// stable within the context of this mark.
class ObjectMonitorContentionMark : StackObj {
DEBUG_ONLY(friend class ObjectMonitor;)
ObjectMonitor* _monitor;
NONCOPYABLE(ObjectMonitorContentionMark);
public:
explicit ObjectMonitorContentionMark(ObjectMonitor* monitor);
~ObjectMonitorContentionMark();
};
#endif // SHARE_RUNTIME_OBJECTMONITOR_HPP

View File

@ -29,9 +29,13 @@
#include "logging/log.hpp"
#include "oops/access.inline.hpp"
#include "oops/markWord.hpp"
#include "runtime/atomic.hpp"
#include "runtime/globals.hpp"
#include "runtime/lockStack.inline.hpp"
#include "runtime/synchronizer.hpp"
#include "utilities/checkedCast.hpp"
#include "utilities/globalDefinitions.hpp"
inline bool ObjectMonitor::is_entered(JavaThread* current) const {
if (LockingMode == LM_LIGHTWEIGHT) {
@ -49,16 +53,38 @@ inline bool ObjectMonitor::is_entered(JavaThread* current) const {
return false;
}
inline markWord ObjectMonitor::header() const {
return Atomic::load(&_header);
inline uintptr_t ObjectMonitor::metadata() const {
return Atomic::load(&_metadata);
}
inline volatile markWord* ObjectMonitor::header_addr() {
return &_header;
inline void ObjectMonitor::set_metadata(uintptr_t value) {
Atomic::store(&_metadata, value);
}
inline volatile uintptr_t* ObjectMonitor::metadata_addr() {
STATIC_ASSERT(std::is_standard_layout<ObjectMonitor>::value);
STATIC_ASSERT(offsetof(ObjectMonitor, _metadata) == 0);
return &_metadata;
}
inline markWord ObjectMonitor::header() const {
assert(!UseObjectMonitorTable, "Lightweight locking with OM table does not use header");
return markWord(metadata());
}
inline void ObjectMonitor::set_header(markWord hdr) {
Atomic::store(&_header, hdr);
assert(!UseObjectMonitorTable, "Lightweight locking with OM table does not use header");
set_metadata(hdr.value());
}
inline intptr_t ObjectMonitor::hash() const {
assert(UseObjectMonitorTable, "Only used by lightweight locking with OM table");
return metadata();
}
inline void ObjectMonitor::set_hash(intptr_t hash) {
assert(UseObjectMonitorTable, "Only used by lightweight locking with OM table");
set_metadata(hash);
}
inline int ObjectMonitor::waiters() const {
@ -180,4 +206,31 @@ inline void ObjectMonitor::set_next_om(ObjectMonitor* new_value) {
Atomic::store(&_next_om, new_value);
}
inline ObjectMonitorContentionMark::ObjectMonitorContentionMark(ObjectMonitor* monitor)
: _monitor(monitor) {
_monitor->add_to_contentions(1);
}
inline ObjectMonitorContentionMark::~ObjectMonitorContentionMark() {
_monitor->add_to_contentions(-1);
}
inline oop ObjectMonitor::object_peek() const {
if (_object.is_null()) {
return nullptr;
}
return _object.peek();
}
inline bool ObjectMonitor::object_is_dead() const {
return object_peek() == nullptr;
}
inline bool ObjectMonitor::object_refers_to(oop obj) const {
if (_object.is_null()) {
return false;
}
return _object.peek() == obj;
}
#endif // SHARE_RUNTIME_OBJECTMONITOR_INLINE_HPP

View File

@ -731,6 +731,11 @@ void ThreadSafepointState::account_safe_thread() {
DEBUG_ONLY(_thread->set_visited_for_critical_count(SafepointSynchronize::safepoint_counter());)
assert(!_safepoint_safe, "Must be unsafe before safe");
_safepoint_safe = true;
// The oops in the monitor cache are cleared to prevent stale cache entries
// from keeping dead objects alive. Because these oops are always cleared
// before safepoint operations they are not visited in JavaThread::oops_do.
_thread->om_clear_monitor_cache();
}
void ThreadSafepointState::restart() {

View File

@ -32,20 +32,21 @@
#include "classfile/vmClasses.hpp"
#include "gc/shared/oopStorage.hpp"
#include "gc/shared/oopStorageSet.hpp"
#include "memory/universe.hpp"
#include "interpreter/oopMapCache.hpp"
#include "memory/universe.hpp"
#include "oops/oopHandle.inline.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiTagMap.hpp"
#include "prims/resolvedMethodTable.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/java.hpp"
#include "runtime/javaCalls.hpp"
#include "runtime/jniHandles.hpp"
#include "runtime/serviceThread.hpp"
#include "runtime/lightweightSynchronizer.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/os.hpp"
#include "prims/jvmtiImpl.hpp"
#include "prims/jvmtiTagMap.hpp"
#include "prims/resolvedMethodTable.hpp"
#include "runtime/serviceThread.hpp"
#include "services/diagnosticArgument.hpp"
#include "services/diagnosticFramework.hpp"
#include "services/finalizerService.hpp"
@ -94,6 +95,7 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
bool cldg_cleanup_work = false;
bool jvmti_tagmap_work = false;
bool oopmap_cache_work = false;
bool object_monitor_table_work = false;
{
// Need state transition ThreadBlockInVM so that this thread
// will be handled by safepoint correctly when this thread is
@ -121,7 +123,8 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
(oop_handles_to_release = JavaThread::has_oop_handles_to_release()) |
(cldg_cleanup_work = ClassLoaderDataGraph::should_clean_metaspaces_and_reset()) |
(jvmti_tagmap_work = JvmtiTagMap::has_object_free_events_and_reset()) |
(oopmap_cache_work = OopMapCache::has_cleanup_work())
(oopmap_cache_work = OopMapCache::has_cleanup_work()) |
(object_monitor_table_work = LightweightSynchronizer::needs_resize())
) == 0) {
// Wait until notified that there is some work to do or timer expires.
// Some cleanup requests don't notify the ServiceThread so work needs to be done at periodic intervals.
@ -183,6 +186,10 @@ void ServiceThread::service_thread_entry(JavaThread* jt, TRAPS) {
if (oopmap_cache_work) {
OopMapCache::cleanup();
}
if (object_monitor_table_work) {
LightweightSynchronizer::resize_table(jt);
}
}
}

View File

@ -58,6 +58,7 @@
#include "prims/nativeLookup.hpp"
#include "runtime/arguments.hpp"
#include "runtime/atomic.hpp"
#include "runtime/basicLock.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/init.hpp"
@ -69,13 +70,14 @@
#include "runtime/sharedRuntime.hpp"
#include "runtime/stackWatermarkSet.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/synchronizer.inline.hpp"
#include "runtime/vframe.inline.hpp"
#include "runtime/vframeArray.hpp"
#include "runtime/vm_version.hpp"
#include "utilities/copy.hpp"
#include "utilities/dtrace.hpp"
#include "utilities/events.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/resourceHash.hpp"
#include "utilities/macros.hpp"
#include "utilities/xmlstream.hpp"
@ -1883,7 +1885,7 @@ void SharedRuntime::monitor_enter_helper(oopDesc* obj, BasicLock* lock, JavaThre
if (!SafepointSynchronize::is_synchronizing()) {
// Only try quick_enter() if we're not trying to reach a safepoint
// so that the calling thread reaches the safepoint more quickly.
if (ObjectSynchronizer::quick_enter(obj, current, lock)) {
if (ObjectSynchronizer::quick_enter(obj, lock, current)) {
return;
}
}
@ -2945,6 +2947,8 @@ JRT_LEAF(intptr_t*, SharedRuntime::OSR_migration_begin( JavaThread *current) )
// Now the displaced header is free to move because the
// object's header no longer refers to it.
buf[i] = (intptr_t)lock->displaced_header().value();
} else if (UseObjectMonitorTable) {
buf[i] = (intptr_t)lock->object_monitor_cache();
}
#ifdef ASSERT
else {

View File

@ -35,12 +35,14 @@
#include "oops/markWord.hpp"
#include "oops/oop.inline.hpp"
#include "runtime/atomic.hpp"
#include "runtime/basicLock.inline.hpp"
#include "runtime/frame.inline.hpp"
#include "runtime/globals.hpp"
#include "runtime/handles.inline.hpp"
#include "runtime/handshake.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/javaThread.hpp"
#include "runtime/lightweightSynchronizer.hpp"
#include "runtime/lockStack.inline.hpp"
#include "runtime/mutexLocker.hpp"
#include "runtime/objectMonitor.hpp"
@ -52,7 +54,7 @@
#include "runtime/safepointVerifiers.hpp"
#include "runtime/sharedRuntime.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/synchronizer.inline.hpp"
#include "runtime/threads.hpp"
#include "runtime/timer.hpp"
#include "runtime/trimNativeHeap.hpp"
@ -276,6 +278,10 @@ void ObjectSynchronizer::initialize() {
// Start the timer for deflations, so it does not trigger immediately.
_last_async_deflation_time_ns = os::javaTimeNanos();
if (LockingMode == LM_LIGHTWEIGHT) {
LightweightSynchronizer::initialize();
}
}
MonitorList ObjectSynchronizer::_in_use_list;
@ -349,7 +355,11 @@ bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool al
}
if (mark.has_monitor()) {
ObjectMonitor* const mon = mark.monitor();
ObjectMonitor* const mon = read_monitor(current, obj, mark);
if (LockingMode == LM_LIGHTWEIGHT && mon == nullptr) {
// Racing with inflation/deflation go slow path
return false;
}
assert(mon->object() == oop(obj), "invariant");
if (mon->owner() != current) return false; // slow-path for IMS exception
@ -376,6 +386,13 @@ bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool al
return false;
}
static bool useHeavyMonitors() {
#if defined(X86) || defined(AARCH64) || defined(PPC64) || defined(RISCV64) || defined(S390)
return LockingMode == LM_MONITOR;
#else
return false;
#endif
}
// The LockNode emitted directly at the synchronization site would have
// been too big if it were to have included support for the cases of inflated
@ -383,33 +400,24 @@ bool ObjectSynchronizer::quick_notify(oopDesc* obj, JavaThread* current, bool al
// Note that we can't safely call AsyncPrintJavaStack() from within
// quick_enter() as our thread state remains _in_Java.
bool ObjectSynchronizer::quick_enter(oop obj, JavaThread* current,
BasicLock * lock) {
bool ObjectSynchronizer::quick_enter_legacy(oop obj, BasicLock* lock, JavaThread* current) {
assert(current->thread_state() == _thread_in_Java, "invariant");
NoSafepointVerifier nsv;
if (obj == nullptr) return false; // Need to throw NPE
if (obj->klass()->is_value_based()) {
return false;
if (useHeavyMonitors()) {
return false; // Slow path
}
if (LockingMode == LM_LIGHTWEIGHT) {
LockStack& lock_stack = current->lock_stack();
if (lock_stack.is_full()) {
// Always go into runtime if the lock stack is full.
return false;
}
if (lock_stack.try_recursive_enter(obj)) {
// Recursive lock successful.
current->inc_held_monitor_count();
return true;
}
return LightweightSynchronizer::quick_enter(obj, lock, current);
}
assert(LockingMode == LM_LEGACY, "legacy mode below");
const markWord mark = obj->mark();
if (mark.has_monitor()) {
ObjectMonitor* const m = mark.monitor();
ObjectMonitor* const m = read_monitor(mark);
// An async deflation or GC can race us before we manage to make
// the ObjectMonitor busy by setting the owner below. If we detect
// that race we just bail out to the slow-path here.
@ -429,18 +437,16 @@ bool ObjectSynchronizer::quick_enter(oop obj, JavaThread* current,
return true;
}
if (LockingMode != LM_LIGHTWEIGHT) {
// This Java Monitor is inflated so obj's header will never be
// displaced to this thread's BasicLock. Make the displaced header
// non-null so this BasicLock is not seen as recursive nor as
// being locked. We do this unconditionally so that this thread's
// BasicLock cannot be mis-interpreted by any stack walkers. For
// performance reasons, stack walkers generally first check for
// stack-locking in the object's header, the second check is for
// recursive stack-locking in the displaced header in the BasicLock,
// and last are the inflated Java Monitor (ObjectMonitor) checks.
lock->set_displaced_header(markWord::unused_mark());
}
// This Java Monitor is inflated so obj's header will never be
// displaced to this thread's BasicLock. Make the displaced header
// non-null so this BasicLock is not seen as recursive nor as
// being locked. We do this unconditionally so that this thread's
// BasicLock cannot be mis-interpreted by any stack walkers. For
// performance reasons, stack walkers generally first check for
// stack-locking in the object's header, the second check is for
// recursive stack-locking in the displaced header in the BasicLock,
// and last are the inflated Java Monitor (ObjectMonitor) checks.
lock->set_displaced_header(markWord::unused_mark());
if (owner == nullptr && m->try_set_owner_from(nullptr, current) == nullptr) {
assert(m->_recursions == 0, "invariant");
@ -508,14 +514,6 @@ void ObjectSynchronizer::handle_sync_on_value_based_class(Handle obj, JavaThread
}
}
static bool useHeavyMonitors() {
#if defined(X86) || defined(AARCH64) || defined(PPC64) || defined(RISCV64) || defined(S390)
return LockingMode == LM_MONITOR;
#else
return false;
#endif
}
// -----------------------------------------------------------------------------
// Monitor Enter/Exit
@ -524,6 +522,11 @@ void ObjectSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* lock
// the locking_thread with respect to the current thread. Currently only used when
// deoptimizing and re-locking locks. See Deoptimization::relock_objects
assert(locking_thread == Thread::current() || locking_thread->is_obj_deopt_suspend(), "must be");
if (LockingMode == LM_LIGHTWEIGHT) {
return LightweightSynchronizer::enter_for(obj, lock, locking_thread);
}
if (!enter_fast_impl(obj, lock, locking_thread)) {
// Inflated ObjectMonitor::enter_for is required
@ -540,8 +543,7 @@ void ObjectSynchronizer::enter_for(Handle obj, BasicLock* lock, JavaThread* lock
}
}
void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current) {
assert(current == Thread::current(), "must be");
void ObjectSynchronizer::enter_legacy(Handle obj, BasicLock* lock, JavaThread* current) {
if (!enter_fast_impl(obj, lock, current)) {
// Inflated ObjectMonitor::enter is required
@ -561,6 +563,7 @@ void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current)
// of this algorithm. Make sure to update that code if the following function is
// changed. The implementation is extremely sensitive to race condition. Be careful.
bool ObjectSynchronizer::enter_fast_impl(Handle obj, BasicLock* lock, JavaThread* locking_thread) {
assert(LockingMode != LM_LIGHTWEIGHT, "Use LightweightSynchronizer");
if (obj->klass()->is_value_based()) {
handle_sync_on_value_based_class(obj, locking_thread);
@ -569,61 +572,7 @@ bool ObjectSynchronizer::enter_fast_impl(Handle obj, BasicLock* lock, JavaThread
locking_thread->inc_held_monitor_count();
if (!useHeavyMonitors()) {
if (LockingMode == LM_LIGHTWEIGHT) {
// Fast-locking does not use the 'lock' argument.
LockStack& lock_stack = locking_thread->lock_stack();
if (lock_stack.is_full()) {
// We unconditionally make room on the lock stack by inflating
// the least recently locked object on the lock stack.
// About the choice to inflate least recently locked object.
// First we must chose to inflate a lock, either some lock on
// the lock-stack or the lock that is currently being entered
// (which may or may not be on the lock-stack).
// Second the best lock to inflate is a lock which is entered
// in a control flow where there are only a very few locks being
// used, as the costly part of inflated locking is inflation,
// not locking. But this property is entirely program dependent.
// Third inflating the lock currently being entered on when it
// is not present on the lock-stack will result in a still full
// lock-stack. This creates a scenario where every deeper nested
// monitorenter must call into the runtime.
// The rational here is as follows:
// Because we cannot (currently) figure out the second, and want
// to avoid the third, we inflate a lock on the lock-stack.
// The least recently locked lock is chosen as it is the lock
// with the longest critical section.
log_info(monitorinflation)("LockStack capacity exceeded, inflating.");
ObjectMonitor* monitor = inflate_for(locking_thread, lock_stack.bottom(), inflate_cause_vm_internal);
assert(monitor->owner() == locking_thread, "must be owner=" PTR_FORMAT " locking_thread=" PTR_FORMAT " mark=" PTR_FORMAT,
p2i(monitor->owner()), p2i(locking_thread), monitor->object()->mark_acquire().value());
assert(!lock_stack.is_full(), "must have made room here");
}
markWord mark = obj()->mark_acquire();
while (mark.is_unlocked()) {
// Retry until a lock state change has been observed. cas_set_mark() may collide with non lock bits modifications.
// Try to swing into 'fast-locked' state.
assert(!lock_stack.contains(obj()), "thread must not already hold the lock");
const markWord locked_mark = mark.set_fast_locked();
const markWord old_mark = obj()->cas_set_mark(locked_mark, mark);
if (old_mark == mark) {
// Successfully fast-locked, push object to lock-stack and return.
lock_stack.push(obj());
return true;
}
mark = old_mark;
}
if (mark.is_fast_locked() && lock_stack.try_recursive_enter(obj())) {
// Recursive lock successful.
return true;
}
// Failed to fast lock.
return false;
} else if (LockingMode == LM_LEGACY) {
if (LockingMode == LM_LEGACY) {
markWord mark = obj->mark();
if (mark.is_unlocked()) {
// Anticipate successful CAS -- the ST of the displaced mark must
@ -656,37 +605,12 @@ bool ObjectSynchronizer::enter_fast_impl(Handle obj, BasicLock* lock, JavaThread
return false;
}
void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) {
current->dec_held_monitor_count();
void ObjectSynchronizer::exit_legacy(oop object, BasicLock* lock, JavaThread* current) {
assert(LockingMode != LM_LIGHTWEIGHT, "Use LightweightSynchronizer");
if (!useHeavyMonitors()) {
markWord mark = object->mark();
if (LockingMode == LM_LIGHTWEIGHT) {
// Fast-locking does not use the 'lock' argument.
LockStack& lock_stack = current->lock_stack();
if (mark.is_fast_locked() && lock_stack.try_recursive_exit(object)) {
// Recursively unlocked.
return;
}
if (mark.is_fast_locked() && lock_stack.is_recursive(object)) {
// This lock is recursive but is not at the top of the lock stack so we're
// doing an unbalanced exit. We have to fall thru to inflation below and
// let ObjectMonitor::exit() do the unlock.
} else {
while (mark.is_fast_locked()) {
// Retry until a lock state change has been observed. cas_set_mark() may collide with non lock bits modifications.
const markWord unlocked_mark = mark.set_unlocked();
const markWord old_mark = object->cas_set_mark(unlocked_mark, mark);
if (old_mark == mark) {
size_t recursions = lock_stack.remove(object) - 1;
assert(recursions == 0, "must not be recursive here");
return;
}
mark = old_mark;
}
}
} else if (LockingMode == LM_LEGACY) {
if (LockingMode == LM_LEGACY) {
markWord dhw = lock->displaced_header();
if (dhw.value() == 0) {
// If the displaced header is null, then this exit matches up with
@ -708,7 +632,7 @@ void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current)
// Monitor owner's stack and update the BasicLocks because a
// Java Monitor can be asynchronously inflated by a thread that
// does not own the Java Monitor.
ObjectMonitor* m = mark.monitor();
ObjectMonitor* m = read_monitor(mark);
assert(m->object()->mark() == mark, "invariant");
assert(m->is_entered(current), "invariant");
}
@ -752,8 +676,16 @@ void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) {
// enter() can make the ObjectMonitor busy. enter() returns false if
// we have lost the race to async deflation and we simply try again.
while (true) {
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_jni_enter);
if (monitor->enter(current)) {
ObjectMonitor* monitor;
bool entered;
if (LockingMode == LM_LIGHTWEIGHT) {
entered = LightweightSynchronizer::inflate_and_enter(obj(), inflate_cause_jni_enter, current, current) != nullptr;
} else {
monitor = inflate(current, obj(), inflate_cause_jni_enter);
entered = monitor->enter(current);
}
if (entered) {
current->inc_held_monitor_count(1, true);
break;
}
@ -765,9 +697,14 @@ void ObjectSynchronizer::jni_enter(Handle obj, JavaThread* current) {
void ObjectSynchronizer::jni_exit(oop obj, TRAPS) {
JavaThread* current = THREAD;
// The ObjectMonitor* can't be async deflated until ownership is
// dropped inside exit() and the ObjectMonitor* must be !is_busy().
ObjectMonitor* monitor = inflate(current, obj, inflate_cause_jni_exit);
ObjectMonitor* monitor;
if (LockingMode == LM_LIGHTWEIGHT) {
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj, inflate_cause_jni_exit, CHECK);
} else {
// The ObjectMonitor* can't be async deflated until ownership is
// dropped inside exit() and the ObjectMonitor* must be !is_busy().
monitor = inflate(current, obj, inflate_cause_jni_exit);
}
// If this thread has locked the object, exit the monitor. We
// intentionally do not use CHECK on check_owner because we must exit the
// monitor even if an exception was already pending.
@ -800,15 +737,22 @@ ObjectLocker::~ObjectLocker() {
// -----------------------------------------------------------------------------
// Wait/Notify/NotifyAll
// NOTE: must use heavy weight monitor to handle wait()
int ObjectSynchronizer::wait(Handle obj, jlong millis, TRAPS) {
JavaThread* current = THREAD;
if (millis < 0) {
THROW_MSG_0(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
}
// The ObjectMonitor* can't be async deflated because the _waiters
// field is incremented before ownership is dropped and decremented
// after ownership is regained.
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_wait);
ObjectMonitor* monitor;
if (LockingMode == LM_LIGHTWEIGHT) {
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj(), inflate_cause_wait, CHECK_0);
} else {
// The ObjectMonitor* can't be async deflated because the _waiters
// field is incremented before ownership is dropped and decremented
// after ownership is regained.
monitor = inflate(current, obj(), inflate_cause_wait);
}
DTRACE_MONITOR_WAIT_PROBE(monitor, obj(), current, millis);
monitor->wait(millis, true, THREAD); // Not CHECK as we need following code
@ -825,9 +769,14 @@ void ObjectSynchronizer::waitUninterruptibly(Handle obj, jlong millis, TRAPS) {
if (millis < 0) {
THROW_MSG(vmSymbols::java_lang_IllegalArgumentException(), "timeout value is negative");
}
ObjectSynchronizer::inflate(THREAD,
obj(),
inflate_cause_wait)->wait(millis, false, THREAD);
ObjectMonitor* monitor;
if (LockingMode == LM_LIGHTWEIGHT) {
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj(), inflate_cause_wait, CHECK);
} else {
monitor = inflate(THREAD, obj(), inflate_cause_wait);
}
monitor->wait(millis, false, THREAD);
}
@ -846,9 +795,15 @@ void ObjectSynchronizer::notify(Handle obj, TRAPS) {
return;
}
}
// The ObjectMonitor* can't be async deflated until ownership is
// dropped by the calling thread.
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_notify);
ObjectMonitor* monitor;
if (LockingMode == LM_LIGHTWEIGHT) {
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj(), inflate_cause_notify, CHECK);
} else {
// The ObjectMonitor* can't be async deflated until ownership is
// dropped by the calling thread.
monitor = inflate(current, obj(), inflate_cause_notify);
}
monitor->notify(CHECK);
}
@ -868,9 +823,15 @@ void ObjectSynchronizer::notifyall(Handle obj, TRAPS) {
return;
}
}
// The ObjectMonitor* can't be async deflated until ownership is
// dropped by the calling thread.
ObjectMonitor* monitor = inflate(current, obj(), inflate_cause_notify);
ObjectMonitor* monitor;
if (LockingMode == LM_LIGHTWEIGHT) {
monitor = LightweightSynchronizer::inflate_locked_or_imse(obj(), inflate_cause_notify, CHECK);
} else {
// The ObjectMonitor* can't be async deflated until ownership is
// dropped by the calling thread.
monitor = inflate(current, obj(), inflate_cause_notify);
}
monitor->notifyAll(CHECK);
}
@ -968,7 +929,7 @@ static markWord read_stable_mark(oop obj) {
// There are simple ways to "diffuse" the middle address bits over the
// generated hashCode values:
static inline intptr_t get_next_hash(Thread* current, oop obj) {
static intptr_t get_next_hash(Thread* current, oop obj) {
intptr_t value = 0;
if (hashCode == 0) {
// This form uses global Park-Miller RNG.
@ -1008,7 +969,33 @@ static inline intptr_t get_next_hash(Thread* current, oop obj) {
return value;
}
static intptr_t install_hash_code(Thread* current, oop obj) {
assert(UseObjectMonitorTable && LockingMode == LM_LIGHTWEIGHT, "must be");
markWord mark = obj->mark_acquire();
for (;;) {
intptr_t hash = mark.hash();
if (hash != 0) {
return hash;
}
hash = get_next_hash(current, obj);
const markWord old_mark = mark;
const markWord new_mark = old_mark.copy_set_hash(hash);
mark = obj->cas_set_mark(new_mark, old_mark);
if (old_mark == mark) {
return hash;
}
}
}
intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
if (UseObjectMonitorTable) {
// Since the monitor isn't in the object header, the hash can simply be
// installed in the object header.
return install_hash_code(current, obj);
}
while (true) {
ObjectMonitor* monitor = nullptr;
@ -1102,7 +1089,7 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
hash = get_next_hash(current, obj); // get a new hash
temp = mark.copy_set_hash(hash) ; // merge the hash into header
assert(temp.is_neutral(), "invariant: header=" INTPTR_FORMAT, temp.value());
uintptr_t v = Atomic::cmpxchg((volatile uintptr_t*)monitor->header_addr(), mark.value(), temp.value());
uintptr_t v = Atomic::cmpxchg(monitor->metadata_addr(), mark.value(), temp.value());
test = markWord(v);
if (test != mark) {
// The attempt to update the ObjectMonitor's header/dmw field
@ -1114,7 +1101,7 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
assert(test.is_neutral(), "invariant: header=" INTPTR_FORMAT, test.value());
assert(hash != 0, "should only have lost the race to a thread that set a non-zero hash");
}
if (monitor->is_being_async_deflated()) {
if (monitor->is_being_async_deflated() && !UseObjectMonitorTable) {
// If we detect that async deflation has occurred, then we
// attempt to restore the header/dmw to the object's header
// so that we only retry once if the deflater thread happens
@ -1145,11 +1132,25 @@ bool ObjectSynchronizer::current_thread_holds_lock(JavaThread* current,
return current->lock_stack().contains(h_obj());
}
if (mark.has_monitor()) {
while (LockingMode == LM_LIGHTWEIGHT && mark.has_monitor()) {
ObjectMonitor* monitor = read_monitor(current, obj, mark);
if (monitor != nullptr) {
return monitor->is_entered(current) != 0;
}
// Racing with inflation/deflation, retry
mark = obj->mark_acquire();
if (mark.is_fast_locked()) {
// Some other thread fast_locked, current could not have held the lock
return false;
}
}
if (LockingMode != LM_LIGHTWEIGHT && mark.has_monitor()) {
// Inflated monitor so header points to ObjectMonitor (tagged pointer).
// The first stage of async deflation does not affect any field
// used by this comparison so the ObjectMonitor* is usable here.
ObjectMonitor* monitor = mark.monitor();
ObjectMonitor* monitor = read_monitor(mark);
return monitor->is_entered(current) != 0;
}
// Unlocked case, header in place
@ -1173,11 +1174,25 @@ JavaThread* ObjectSynchronizer::get_lock_owner(ThreadsList * t_list, Handle h_ob
return Threads::owning_thread_from_object(t_list, h_obj());
}
if (mark.has_monitor()) {
while (LockingMode == LM_LIGHTWEIGHT && mark.has_monitor()) {
ObjectMonitor* monitor = read_monitor(Thread::current(), obj, mark);
if (monitor != nullptr) {
return Threads::owning_thread_from_monitor(t_list, monitor);
}
// Racing with inflation/deflation, retry
mark = obj->mark_acquire();
if (mark.is_fast_locked()) {
// Some other thread fast_locked
return Threads::owning_thread_from_object(t_list, h_obj());
}
}
if (LockingMode != LM_LIGHTWEIGHT && mark.has_monitor()) {
// Inflated monitor so header points to ObjectMonitor (tagged pointer).
// The first stage of async deflation does not affect any field
// used by this comparison so the ObjectMonitor* is usable here.
ObjectMonitor* monitor = mark.monitor();
ObjectMonitor* monitor = read_monitor(mark);
assert(monitor != nullptr, "monitor should be non-null");
// owning_thread_from_monitor() may also return null here:
return Threads::owning_thread_from_monitor(t_list, monitor);
@ -1389,9 +1404,10 @@ static void post_monitor_inflate_event(EventJavaMonitorInflate* event,
// Fast path code shared by multiple functions
void ObjectSynchronizer::inflate_helper(oop obj) {
assert(LockingMode != LM_LIGHTWEIGHT, "only inflate through enter");
markWord mark = obj->mark_acquire();
if (mark.has_monitor()) {
ObjectMonitor* monitor = mark.monitor();
ObjectMonitor* monitor = read_monitor(mark);
markWord dmw = monitor->header();
assert(dmw.is_neutral(), "sanity check: header=" INTPTR_FORMAT, dmw.value());
return;
@ -1401,39 +1417,25 @@ void ObjectSynchronizer::inflate_helper(oop obj) {
ObjectMonitor* ObjectSynchronizer::inflate(Thread* current, oop obj, const InflateCause cause) {
assert(current == Thread::current(), "must be");
if (LockingMode == LM_LIGHTWEIGHT && current->is_Java_thread()) {
return inflate_impl(JavaThread::cast(current), obj, cause);
}
return inflate_impl(nullptr, obj, cause);
assert(LockingMode != LM_LIGHTWEIGHT, "only inflate through enter");
return inflate_impl(obj, cause);
}
ObjectMonitor* ObjectSynchronizer::inflate_for(JavaThread* thread, oop obj, const InflateCause cause) {
assert(thread == Thread::current() || thread->is_obj_deopt_suspend(), "must be");
return inflate_impl(thread, obj, cause);
assert(LockingMode != LM_LIGHTWEIGHT, "LM_LIGHTWEIGHT cannot use inflate_for");
return inflate_impl(obj, cause);
}
ObjectMonitor* ObjectSynchronizer::inflate_impl(JavaThread* inflating_thread, oop object, const InflateCause cause) {
// The JavaThread* inflating_thread parameter is only used by LM_LIGHTWEIGHT and requires
// that the inflating_thread == Thread::current() or is suspended throughout the call by
// some other mechanism.
// Even with LM_LIGHTWEIGHT the thread might be nullptr when called from a non
// JavaThread. (As may still be the case from FastHashCode). However it is only
// important for the correctness of the LM_LIGHTWEIGHT algorithm that the thread
// is set when called from ObjectSynchronizer::enter from the owning thread,
// ObjectSynchronizer::enter_for from any thread, or ObjectSynchronizer::exit.
ObjectMonitor* ObjectSynchronizer::inflate_impl(oop object, const InflateCause cause) {
assert(LockingMode != LM_LIGHTWEIGHT, "LM_LIGHTWEIGHT cannot use inflate_impl");
EventJavaMonitorInflate event;
for (;;) {
const markWord mark = object->mark_acquire();
// The mark can be in one of the following states:
// * inflated - Just return if using stack-locking.
// If using fast-locking and the ObjectMonitor owner
// is anonymous and the inflating_thread owns the
// object lock, then we make the inflating_thread
// the ObjectMonitor owner and remove the lock from
// the inflating_thread's lock stack.
// * fast-locked - Coerce it to inflated from fast-locked.
// * inflated - Just return it.
// * stack-locked - Coerce it to inflated from stack-locked.
// * INFLATING - Busy wait for conversion from stack-locked to
// inflated.
@ -1444,80 +1446,18 @@ ObjectMonitor* ObjectSynchronizer::inflate_impl(JavaThread* inflating_thread, oo
ObjectMonitor* inf = mark.monitor();
markWord dmw = inf->header();
assert(dmw.is_neutral(), "invariant: header=" INTPTR_FORMAT, dmw.value());
if (LockingMode == LM_LIGHTWEIGHT && inf->is_owner_anonymous() &&
inflating_thread != nullptr && inflating_thread->lock_stack().contains(object)) {
inf->set_owner_from_anonymous(inflating_thread);
size_t removed = inflating_thread->lock_stack().remove(object);
inf->set_recursions(removed - 1);
}
return inf;
}
if (LockingMode != LM_LIGHTWEIGHT) {
// New lightweight locking does not use INFLATING.
// CASE: inflation in progress - inflating over a stack-lock.
// Some other thread is converting from stack-locked to inflated.
// Only that thread can complete inflation -- other threads must wait.
// The INFLATING value is transient.
// Currently, we spin/yield/park and poll the markword, waiting for inflation to finish.
// We could always eliminate polling by parking the thread on some auxiliary list.
if (mark == markWord::INFLATING()) {
read_stable_mark(object);
continue;
}
}
// CASE: fast-locked
// Could be fast-locked either by the inflating_thread or by some other thread.
//
// Note that we allocate the ObjectMonitor speculatively, _before_
// attempting to set the object's mark to the new ObjectMonitor. If
// the inflating_thread owns the monitor, then we set the ObjectMonitor's
// owner to the inflating_thread. Otherwise, we set the ObjectMonitor's owner
// to anonymous. If we lose the race to set the object's mark to the
// new ObjectMonitor, then we just delete it and loop around again.
//
LogStreamHandle(Trace, monitorinflation) lsh;
if (LockingMode == LM_LIGHTWEIGHT && mark.is_fast_locked()) {
ObjectMonitor* monitor = new ObjectMonitor(object);
monitor->set_header(mark.set_unlocked());
bool own = inflating_thread != nullptr && inflating_thread->lock_stack().contains(object);
if (own) {
// Owned by inflating_thread.
monitor->set_owner_from(nullptr, inflating_thread);
} else {
// Owned by somebody else.
monitor->set_owner_anonymous();
}
markWord monitor_mark = markWord::encode(monitor);
markWord old_mark = object->cas_set_mark(monitor_mark, mark);
if (old_mark == mark) {
// Success! Return inflated monitor.
if (own) {
size_t removed = inflating_thread->lock_stack().remove(object);
monitor->set_recursions(removed - 1);
}
// Once the ObjectMonitor is configured and object is associated
// with the ObjectMonitor, it is safe to allow async deflation:
_in_use_list.add(monitor);
// Hopefully the performance counters are allocated on distinct
// cache lines to avoid false sharing on MP systems ...
OM_PERFDATA_OP(Inflations, inc());
if (log_is_enabled(Trace, monitorinflation)) {
ResourceMark rm;
lsh.print_cr("inflate(has_locker): object=" INTPTR_FORMAT ", mark="
INTPTR_FORMAT ", type='%s'", p2i(object),
object->mark().value(), object->klass()->external_name());
}
if (event.should_commit()) {
post_monitor_inflate_event(&event, object, cause);
}
return monitor;
} else {
delete monitor;
continue; // Interference -- just retry
}
// CASE: inflation in progress - inflating over a stack-lock.
// Some other thread is converting from stack-locked to inflated.
// Only that thread can complete inflation -- other threads must wait.
// The INFLATING value is transient.
// Currently, we spin/yield/park and poll the markword, waiting for inflation to finish.
// We could always eliminate polling by parking the thread on some auxiliary list.
if (mark == markWord::INFLATING()) {
read_stable_mark(object);
continue;
}
// CASE: stack-locked
@ -1531,8 +1471,8 @@ ObjectMonitor* ObjectSynchronizer::inflate_impl(JavaThread* inflating_thread, oo
// the odds of inflation contention. If we lose the race to set INFLATING,
// then we just delete the ObjectMonitor and loop around again.
//
LogStreamHandle(Trace, monitorinflation) lsh;
if (LockingMode == LM_LEGACY && mark.has_locker()) {
assert(LockingMode != LM_LIGHTWEIGHT, "cannot happen with new lightweight locking");
ObjectMonitor* m = new ObjectMonitor(object);
// Optimistically prepare the ObjectMonitor - anticipate successful CAS
// We do this before the CAS in order to minimize the length of time
@ -1664,13 +1604,14 @@ ObjectMonitor* ObjectSynchronizer::inflate_impl(JavaThread* inflating_thread, oo
size_t ObjectSynchronizer::deflate_monitor_list(ObjectMonitorDeflationSafepointer* safepointer) {
MonitorList::Iterator iter = _in_use_list.iterator();
size_t deflated_count = 0;
Thread* current = Thread::current();
while (iter.has_next()) {
if (deflated_count >= (size_t)MonitorDeflationMax) {
break;
}
ObjectMonitor* mid = iter.next();
if (mid->deflate_monitor()) {
if (mid->deflate_monitor(current)) {
deflated_count++;
}
@ -1688,6 +1629,11 @@ class HandshakeForDeflation : public HandshakeClosure {
void do_thread(Thread* thread) {
log_trace(monitorinflation)("HandshakeForDeflation::do_thread: thread="
INTPTR_FORMAT, p2i(thread));
if (thread->is_Java_thread()) {
// Clear OM cache
JavaThread* jt = JavaThread::cast(thread);
jt->om_clear_monitor_cache();
}
}
};
@ -1834,6 +1780,14 @@ size_t ObjectSynchronizer::deflate_idle_monitors() {
GrowableArray<ObjectMonitor*> delete_list((int)deflated_count);
unlinked_count = _in_use_list.unlink_deflated(deflated_count, &delete_list, &safepointer);
#ifdef ASSERT
if (UseObjectMonitorTable) {
for (ObjectMonitor* monitor : delete_list) {
assert(!LightweightSynchronizer::contains_monitor(current, monitor), "Should have been removed");
}
}
#endif
log.before_handshake(unlinked_count);
// A JavaThread needs to handshake in order to safely free the
@ -2042,29 +1996,35 @@ void ObjectSynchronizer::chk_in_use_entry(ObjectMonitor* n, outputStream* out,
return;
}
if (n->header().value() == 0) {
if (n->metadata() == 0) {
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor must "
"have non-null _header field.", p2i(n));
"have non-null _metadata (header/hash) field.", p2i(n));
*error_cnt_p = *error_cnt_p + 1;
}
const oop obj = n->object_peek();
if (obj != nullptr) {
const markWord mark = obj->mark();
if (!mark.has_monitor()) {
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
"object does not think it has a monitor: obj="
INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n),
p2i(obj), mark.value());
*error_cnt_p = *error_cnt_p + 1;
}
ObjectMonitor* const obj_mon = mark.monitor();
if (n != obj_mon) {
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
"object does not refer to the same monitor: obj="
INTPTR_FORMAT ", mark=" INTPTR_FORMAT ", obj_mon="
INTPTR_FORMAT, p2i(n), p2i(obj), mark.value(), p2i(obj_mon));
*error_cnt_p = *error_cnt_p + 1;
}
if (obj == nullptr) {
return;
}
const markWord mark = obj->mark();
if (!mark.has_monitor()) {
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
"object does not think it has a monitor: obj="
INTPTR_FORMAT ", mark=" INTPTR_FORMAT, p2i(n),
p2i(obj), mark.value());
*error_cnt_p = *error_cnt_p + 1;
return;
}
ObjectMonitor* const obj_mon = read_monitor(Thread::current(), obj, mark);
if (n != obj_mon) {
out->print_cr("ERROR: monitor=" INTPTR_FORMAT ": in-use monitor's "
"object does not refer to the same monitor: obj="
INTPTR_FORMAT ", mark=" INTPTR_FORMAT ", obj_mon="
INTPTR_FORMAT, p2i(n), p2i(obj), mark.value(), p2i(obj_mon));
*error_cnt_p = *error_cnt_p + 1;
}
}
@ -2087,10 +2047,10 @@ void ObjectSynchronizer::log_in_use_monitor_details(outputStream* out, bool log_
monitors_iterate([&](ObjectMonitor* monitor) {
if (is_interesting(monitor)) {
const oop obj = monitor->object_peek();
const markWord mark = monitor->header();
const intptr_t hash = UseObjectMonitorTable ? monitor->hash() : monitor->header().hash();
ResourceMark rm;
out->print(INTPTR_FORMAT " %d%d%d " INTPTR_FORMAT " %s", p2i(monitor),
monitor->is_busy(), mark.hash() != 0, monitor->owner() != nullptr,
monitor->is_busy(), hash != 0, monitor->owner() != nullptr,
p2i(obj), obj == nullptr ? "" : obj->klass()->external_name());
if (monitor->is_busy()) {
out->print(" (%s)", monitor->is_busy_to_string(&ss));

View File

@ -29,6 +29,7 @@
#include "oops/markWord.hpp"
#include "runtime/basicLock.hpp"
#include "runtime/handles.hpp"
#include "runtime/javaThread.hpp"
#include "utilities/resourceHash.hpp"
template <typename T> class GrowableArray;
@ -93,8 +94,9 @@ class ObjectSynchronizer : AllStatic {
// deoptimization at monitor exit. Hence, it does not take a Handle argument.
// This is the "slow path" version of monitor enter and exit.
static void enter(Handle obj, BasicLock* lock, JavaThread* current);
static void exit(oop obj, BasicLock* lock, JavaThread* current);
static inline void enter(Handle obj, BasicLock* lock, JavaThread* current);
static inline void exit(oop obj, BasicLock* lock, JavaThread* current);
// Used to enter a monitor for another thread. This requires that the
// locking_thread is suspended, and that entering on a potential
// inflated monitor may only contend with deflation. That is the obj being
@ -106,6 +108,9 @@ private:
// inflated monitor enter.
static bool enter_fast_impl(Handle obj, BasicLock* lock, JavaThread* locking_thread);
static bool quick_enter_legacy(oop obj, BasicLock* Lock, JavaThread* current);
static void enter_legacy(Handle obj, BasicLock* Lock, JavaThread* current);
static void exit_legacy(oop obj, BasicLock* lock, JavaThread* current);
public:
// Used only to handle jni locks or other unmatched monitor enter/exit
// Internally they will use heavy weight monitor.
@ -118,7 +123,7 @@ public:
static void notifyall(Handle obj, TRAPS);
static bool quick_notify(oopDesc* obj, JavaThread* current, bool All);
static bool quick_enter(oop obj, JavaThread* current, BasicLock* Lock);
static inline bool quick_enter(oop obj, BasicLock* Lock, JavaThread* current);
// Special internal-use-only method for use by JVM infrastructure
// that needs to wait() on a java-level object but that can't risk
@ -132,13 +137,16 @@ public:
private:
// Shared implementation between the different LockingMode.
static ObjectMonitor* inflate_impl(JavaThread* thread, oop obj, const InflateCause cause);
static ObjectMonitor* inflate_impl(oop obj, const InflateCause cause);
public:
// This version is only for internal use
static void inflate_helper(oop obj);
static const char* inflate_cause_name(const InflateCause cause);
inline static ObjectMonitor* read_monitor(markWord mark);
inline static ObjectMonitor* read_monitor(Thread* current, oop obj, markWord mark);
// Returns the identity hash value for an oop
// NOTE: It may cause monitor inflation
static intptr_t FastHashCode(Thread* current, oop obj);
@ -200,6 +208,7 @@ public:
private:
friend class SynchronizerTest;
friend class LightweightSynchronizer;
static MonitorList _in_use_list;
static volatile bool _is_async_deflation_requested;

View File

@ -0,0 +1,81 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*
*/
#ifndef SHARE_RUNTIME_SYNCHRONIZER_INLINE_HPP
#define SHARE_RUNTIME_SYNCHRONIZER_INLINE_HPP
#include "runtime/synchronizer.hpp"
#include "runtime/lightweightSynchronizer.hpp"
#include "runtime/safepointVerifiers.hpp"
inline ObjectMonitor* ObjectSynchronizer::read_monitor(markWord mark) {
return mark.monitor();
}
inline ObjectMonitor* ObjectSynchronizer::read_monitor(Thread* current, oop obj, markWord mark) {
if (!UseObjectMonitorTable) {
return read_monitor(mark);
} else {
return LightweightSynchronizer::get_monitor_from_table(current, obj);
}
}
inline void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current) {
assert(current == Thread::current(), "must be");
if (LockingMode == LM_LIGHTWEIGHT) {
LightweightSynchronizer::enter(obj, lock, current);
} else {
enter_legacy(obj, lock, current);
}
}
inline bool ObjectSynchronizer::quick_enter(oop obj, BasicLock* lock, JavaThread* current) {
assert(current->thread_state() == _thread_in_Java, "invariant");
NoSafepointVerifier nsv;
if (obj == nullptr) return false; // Need to throw NPE
if (obj->klass()->is_value_based()) {
return false;
}
if (LockingMode == LM_LIGHTWEIGHT) {
return LightweightSynchronizer::quick_enter(obj, lock, current);
} else {
return quick_enter_legacy(obj, lock, current);
}
}
inline void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current) {
current->dec_held_monitor_count();
if (LockingMode == LM_LIGHTWEIGHT) {
LightweightSynchronizer::exit(object, current);
} else {
exit_legacy(object, lock, current);
}
}
#endif // SHARE_RUNTIME_SYNCHRONIZER_INLINE_HPP

View File

@ -50,7 +50,7 @@
#include "runtime/signature.hpp"
#include "runtime/stackFrameStream.inline.hpp"
#include "runtime/stubRoutines.hpp"
#include "runtime/synchronizer.hpp"
#include "runtime/synchronizer.inline.hpp"
#include "runtime/vframe.inline.hpp"
#include "runtime/vframeArray.hpp"
#include "runtime/vframe_hp.hpp"
@ -247,13 +247,16 @@ void javaVFrame::print_lock_info_on(outputStream* st, int frame_count) {
markWord mark = monitor->owner()->mark();
// The first stage of async deflation does not affect any field
// used by this comparison so the ObjectMonitor* is usable here.
if (mark.has_monitor() &&
( // we have marked ourself as pending on this monitor
mark.monitor() == thread()->current_pending_monitor() ||
if (mark.has_monitor()) {
ObjectMonitor* mon = ObjectSynchronizer::read_monitor(current, monitor->owner(), mark);
if (// if the monitor is null we must be in the process of locking
mon == nullptr ||
// we have marked ourself as pending on this monitor
mon == thread()->current_pending_monitor() ||
// we are not the owner of this monitor
!mark.monitor()->is_entered(thread())
)) {
lock_state = "waiting to lock";
!mon->is_entered(thread())) {
lock_state = "waiting to lock";
}
}
}
print_locked_object_class_name(st, Handle(current, monitor->owner()), lock_state);

View File

@ -777,11 +777,11 @@
/* Monitors */ \
/************/ \
\
volatile_nonstatic_field(ObjectMonitor, _header, markWord) \
volatile_nonstatic_field(ObjectMonitor, _metadata, uintptr_t) \
unchecked_nonstatic_field(ObjectMonitor, _object, sizeof(void *)) /* NOTE: no type */ \
unchecked_nonstatic_field(ObjectMonitor, _owner, sizeof(void *)) /* NOTE: no type */ \
volatile_nonstatic_field(ObjectMonitor, _next_om, ObjectMonitor*) \
volatile_nonstatic_field(BasicLock, _displaced_header, markWord) \
volatile_nonstatic_field(BasicLock, _metadata, uintptr_t) \
nonstatic_field(ObjectMonitor, _contentions, int) \
volatile_nonstatic_field(ObjectMonitor, _waiters, int) \
volatile_nonstatic_field(ObjectMonitor, _recursions, intx) \

View File

@ -156,6 +156,16 @@ public class Mark extends VMObject {
if (Assert.ASSERTS_ENABLED) {
Assert.that(hasMonitor(), "check");
}
if (VM.getVM().getCommandLineFlag("UseObjectMonitorTable").getBool()) {
Iterator it = ObjectSynchronizer.objectMonitorIterator();
while (it != null && it.hasNext()) {
ObjectMonitor mon = (ObjectMonitor)it.next();
if (getAddress().equals(mon.object())) {
return mon;
}
}
return null;
}
// Use xor instead of &~ to provide one extra tag-bit check.
Address monAddr = valueAsAddress().xorWithMask(monitorValue);
return new ObjectMonitor(monAddr);

View File

@ -43,7 +43,7 @@ public class BasicLock extends VMObject {
private static synchronized void initialize(TypeDataBase db) throws WrongTypeException {
Type type = db.lookupType("BasicLock");
displacedHeaderField = type.getCIntegerField("_displaced_header");
displacedHeaderField = type.getCIntegerField("_metadata");
}
private static CIntegerField displacedHeaderField;

View File

@ -45,8 +45,8 @@ public class ObjectMonitor extends VMObject {
heap = VM.getVM().getObjectHeap();
Type type = db.lookupType("ObjectMonitor");
sun.jvm.hotspot.types.Field f = type.getField("_header");
headerFieldOffset = f.getOffset();
sun.jvm.hotspot.types.Field f = type.getField("_metadata");
metadataFieldOffset = f.getOffset();
f = type.getField("_object");
objectFieldOffset = f.getOffset();
f = type.getField("_owner");
@ -65,7 +65,7 @@ public class ObjectMonitor extends VMObject {
}
public Mark header() {
return new Mark(addr.addOffsetTo(headerFieldOffset));
return new Mark(addr.addOffsetTo(metadataFieldOffset));
}
// FIXME
@ -114,7 +114,7 @@ public class ObjectMonitor extends VMObject {
// vmStructs.cpp because they aren't strongly typed in the VM, or
// would confuse the SA's type system.
private static ObjectHeap heap;
private static long headerFieldOffset;
private static long metadataFieldOffset;
private static long objectFieldOffset;
private static long ownerFieldOffset;
private static long nextOMFieldOffset;

View File

@ -55,6 +55,9 @@ public class ObjectSynchronizer {
// FIXME: can not generate marks in debugging system
return mark.hash();
} else if (mark.hasMonitor()) {
if (VM.getVM().getCommandLineFlag("UseObjectMonitorTable").getBool()) {
return mark.hash();
}
ObjectMonitor monitor = mark.monitor();
Mark temp = monitor.header();
return temp.hash();

View File

@ -27,20 +27,20 @@
#include "unittest.hpp"
TEST_VM(ObjectMonitor, sanity) {
uint cache_line_size = VM_Version::L1_data_cache_line_size();
uint cache_line_size = VM_Version::L1_data_cache_line_size();
if (cache_line_size != 0) {
// We were able to determine the L1 data cache line size so
// do some cache line specific sanity checks
EXPECT_EQ((size_t) 0, sizeof (PaddedEnd<ObjectMonitor>) % cache_line_size)
<< "PaddedEnd<ObjectMonitor> size is not a "
<< "multiple of a cache line which permits false sharing. "
<< "sizeof(PaddedEnd<ObjectMonitor>) = "
<< sizeof (PaddedEnd<ObjectMonitor>)
<< "; cache_line_size = " << cache_line_size;
if (cache_line_size != 0) {
EXPECT_GE((size_t) in_bytes(ObjectMonitor::owner_offset()), cache_line_size)
<< "the _header and _owner fields are closer "
EXPECT_EQ(in_bytes(ObjectMonitor::metadata_offset()), 0)
<< "_metadata at a non 0 offset. metadata_offset = "
<< in_bytes(ObjectMonitor::metadata_offset());
EXPECT_GE((size_t) in_bytes(ObjectMonitor::owner_offset()), cache_line_size)
<< "the _metadata and _owner fields are closer "
<< "than a cache line which permits false sharing.";
EXPECT_GE((size_t) in_bytes(ObjectMonitor::recursions_offset() - ObjectMonitor::owner_offset()), cache_line_size)
<< "the _owner and _recursions fields are closer "
<< "than a cache line which permits false sharing.";
}
}

View File

@ -0,0 +1,244 @@
/*
* Copyright (c) 2024, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test id=NormalDeflation
* @summary A collection of small tests using synchronized, wait, notify to try
* and achieve good cheap coverage of UseObjectMonitorTable.
* @library /test/lib
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
* -XX:+UseObjectMonitorTable
* UseObjectMonitorTableTest
*/
/**
* @test id=ExtremeDeflation
* @summary Run the same tests but with deflation running constantly.
* @library /test/lib
* @run main/othervm -XX:+UnlockDiagnosticVMOptions
* -XX:GuaranteedAsyncDeflationInterval=1
* -XX:+UseObjectMonitorTable
* UseObjectMonitorTableTest
*/
import jdk.test.lib.Utils;
import java.lang.Runnable;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.Random;
import java.util.stream.Stream;
public class UseObjectMonitorTableTest {
static final ThreadFactory TF = Executors.defaultThreadFactory();
static class WaitNotifyTest implements Runnable {
static final int ITERATIONS = 10_000;
static final int THREADS = 10;
final WaitNotifySyncChannel startLatchChannel = new WaitNotifySyncChannel();
final WaitNotifySyncChannel endLatchChannel = new WaitNotifySyncChannel();
int count = 0;
static class WaitNotifyCountDownLatch {
int latch;
WaitNotifyCountDownLatch(int count) {
latch = count;
}
synchronized void await() {
while (latch != 0) {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException("WaitNotifyTest: Unexpected interrupt", e);
}
}
}
synchronized void countDown() {
if (latch != 0) {
latch--;
if (latch == 0) {
notifyAll();
}
}
}
}
static class WaitNotifySyncChannel extends WaitNotifyCountDownLatch {
WaitNotifyCountDownLatch object;
WaitNotifySyncChannel() { super(0); }
synchronized void send(WaitNotifyCountDownLatch object, int count) {
await();
latch = count;
this.object = object;
notifyAll();
}
synchronized WaitNotifyCountDownLatch receive() {
while (latch == 0) {
try {
wait();
} catch (InterruptedException e) {
throw new RuntimeException("WaitNotifyTest: Unexpected interrupt", e);
}
}
countDown();
return object;
}
}
synchronized int getCount() {
return count;
}
synchronized void increment() {
count++;
}
public void run() {
System.out.println("WaitNotifyTest started.");
for (int t = 0; t < THREADS; t++) {
TF.newThread(() -> {
for (int i = 0; i < ITERATIONS; i++) {
startLatchChannel.receive().await();
increment();
endLatchChannel.receive().countDown();
}
}).start();
}
for (int i = 0; i < ITERATIONS; i++) {
WaitNotifyCountDownLatch startLatch = new WaitNotifyCountDownLatch(1);
WaitNotifyCountDownLatch endLatch = new WaitNotifyCountDownLatch(THREADS);
int count = getCount();
if (count != i * THREADS) {
throw new RuntimeException("WaitNotifyTest: Invalid Count " + count +
" pre-iteration " + i);
}
startLatchChannel.send(startLatch, 10);
startLatch.countDown();
endLatchChannel.send(endLatch, 10);
endLatch.await();
}
int count = getCount();
if (count != ITERATIONS * THREADS) {
throw new RuntimeException("WaitNotifyTest: Invalid Count " + count);
}
System.out.println("WaitNotifyTest passed.");
}
}
static class RandomDepthTest implements Runnable {
static final int THREADS = 10;
static final int ITERATIONS = 10_000;
static final int MAX_DEPTH = 20;
static final int MAX_RECURSION_COUNT = 10;
static final double RECURSION_CHANCE = .25;
final Random random = Utils.getRandomInstance();
final Locker lockers[] = new Locker[MAX_DEPTH];
final CyclicBarrier syncBarrier = new CyclicBarrier(THREADS + 1);
int count = 0;
class Locker {
final int depth;
Locker(int depth) {
this.depth = depth;
}
synchronized int getCount() {
if (depth == MAX_DEPTH) {
return count;
}
return lockers[depth].getCount();
}
synchronized void increment(int recursion_count) {
if (recursion_count != MAX_RECURSION_COUNT &&
random.nextDouble() < RECURSION_CHANCE) {
this.increment(recursion_count + 1);
return;
}
if (depth == MAX_DEPTH) {
count++;
return;
}
lockers[depth + random.nextInt(MAX_DEPTH - depth)].increment(recursion_count);
}
synchronized Locker create() {
if (depth != MAX_DEPTH) {
lockers[depth] = (new Locker(depth + 1)).create();
}
return this;
}
}
int getCount() {
return lockers[0].getCount();
}
void increment() {
lockers[random.nextInt(MAX_DEPTH)].increment(0);
}
void create() {
lockers[0] = (new Locker(1)).create();
}
void syncPoint() {
try {
syncBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException("RandomDepthTest: Unexpected interrupt", e);
} catch (BrokenBarrierException e) {
throw new RuntimeException("RandomDepthTest: Unexpected broken barrier", e);
}
}
public void run() {
System.out.println("RandomDepthTest started.");
for (int t = 0; t < THREADS; t++) {
TF.newThread(() -> {
syncPoint();
for (int i = 0; i < ITERATIONS; i++) {
increment();
}
syncPoint();
}).start();
}
create();
syncPoint();
syncPoint();
int count = getCount();
if (count != THREADS * ITERATIONS) {
throw new RuntimeException("RandomDepthTest: Invalid Count " + count);
}
System.out.println("RandomDepthTest passed.");
}
}
public static void main(String[] args) {
Stream.of(
TF.newThread(new WaitNotifyTest()),
TF.newThread(new RandomDepthTest())
).map(t -> {
t.start();
return t;
}).forEach(t -> {
try {
t.join();
} catch (InterruptedException e) {
throw new RuntimeException("UseObjectMonitorTableTest: Unexpected interrupt", e);
}
});
System.out.println("UseObjectMonitorTableTest passed.");
}
}

View File

@ -38,7 +38,7 @@ import jdk.test.lib.process.ProcessTools;
public class MonitorInflationTest {
static void analyzeOutputOn(ProcessBuilder pb) throws Exception {
OutputAnalyzer output = new OutputAnalyzer(pb.start());
output.shouldContain("inflate(has_locker):");
output.shouldContain("inflate:");
output.shouldContain("type='MonitorInflationTest$Waiter'");
output.shouldContain("I've been waiting.");
output.shouldHaveExitValue(0);

View File

@ -54,6 +54,8 @@ public class LockUnlock {
public Object lockObject1;
public Object lockObject2;
public volatile Object lockObject3Inflated;
public volatile Object lockObject4Inflated;
public int factorial;
public int dummyInt1;
public int dummyInt2;
@ -62,13 +64,28 @@ public class LockUnlock {
public void setup() {
lockObject1 = new Object();
lockObject2 = new Object();
lockObject3Inflated = new Object();
lockObject4Inflated = new Object();
// Inflate the lock to use an ObjectMonitor
try {
synchronized (lockObject3Inflated) {
lockObject3Inflated.wait(1);
}
synchronized (lockObject4Inflated) {
lockObject4Inflated.wait(1);
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
dummyInt1 = 47;
dummyInt2 = 11; // anything
}
/** Perform a synchronized on a local object within a loop. */
@Benchmark
public void testSimpleLockUnlock() {
public void testBasicSimpleLockUnlockLocal() {
Object localObject = lockObject1;
for (int i = 0; i < innerCount; i++) {
synchronized (localObject) {
@ -78,9 +95,43 @@ public class LockUnlock {
}
}
/** Perform a synchronized on an object within a loop. */
@Benchmark
public void testBasicSimpleLockUnlock() {
for (int i = 0; i < innerCount; i++) {
synchronized (lockObject1) {
dummyInt1++;
dummyInt2++;
}
}
}
/** Perform a synchronized on a local object within a loop. */
@Benchmark
public void testInflatedSimpleLockUnlockLocal() {
Object localObject = lockObject3Inflated;
for (int i = 0; i < innerCount; i++) {
synchronized (localObject) {
dummyInt1++;
dummyInt2++;
}
}
}
/** Perform a synchronized on an object within a loop. */
@Benchmark
public void testInflatedSimpleLockUnlock() {
for (int i = 0; i < innerCount; i++) {
synchronized (lockObject3Inflated) {
dummyInt1++;
dummyInt2++;
}
}
}
/** Perform a recursive synchronized on a local object within a loop. */
@Benchmark
public void testRecursiveLockUnlock() {
public void testBasicRecursiveLockUnlockLocal() {
Object localObject = lockObject1;
for (int i = 0; i < innerCount; i++) {
synchronized (localObject) {
@ -92,9 +143,22 @@ public class LockUnlock {
}
}
/** Perform a recursive synchronized on an object within a loop. */
@Benchmark
public void testBasicRecursiveLockUnlock() {
for (int i = 0; i < innerCount; i++) {
synchronized (lockObject1) {
synchronized (lockObject1) {
dummyInt1++;
dummyInt2++;
}
}
}
}
/** Perform two synchronized after each other on the same local object. */
@Benchmark
public void testSerialLockUnlock() {
public void testBasicSerialLockUnlockLocal() {
Object localObject = lockObject1;
for (int i = 0; i < innerCount; i++) {
synchronized (localObject) {
@ -106,6 +170,126 @@ public class LockUnlock {
}
}
/** Perform two synchronized after each other on the same object. */
@Benchmark
public void testBasicSerialLockUnlock() {
for (int i = 0; i < innerCount; i++) {
synchronized (lockObject1) {
dummyInt1++;
}
synchronized (lockObject1) {
dummyInt2++;
}
}
}
/** Perform two synchronized after each other on the same local object. */
@Benchmark
public void testInflatedSerialLockUnlockLocal() {
Object localObject = lockObject3Inflated;
for (int i = 0; i < innerCount; i++) {
synchronized (localObject) {
dummyInt1++;
}
synchronized (localObject) {
dummyInt2++;
}
}
}
/** Perform two synchronized after each other on the same object. */
@Benchmark
public void testInflatedSerialLockUnlock() {
for (int i = 0; i < innerCount; i++) {
synchronized (lockObject3Inflated) {
dummyInt1++;
}
synchronized (lockObject3Inflated) {
dummyInt2++;
}
}
}
/** Perform two synchronized after each other on the same object. */
@Benchmark
public void testInflatedMultipleSerialLockUnlock() {
for (int i = 0; i < innerCount; i++) {
synchronized (lockObject3Inflated) {
dummyInt1++;
}
synchronized (lockObject4Inflated) {
dummyInt2++;
}
}
}
/** Perform two synchronized after each other on the same object. */
@Benchmark
public void testInflatedMultipleRecursiveLockUnlock() {
for (int i = 0; i < innerCount; i++) {
synchronized (lockObject3Inflated) {
dummyInt1++;
synchronized (lockObject4Inflated) {
dummyInt2++;
}
}
}
}
/** Perform a recursive-only synchronized on a local object within a loop. */
@Benchmark
public void testInflatedRecursiveOnlyLockUnlockLocal() {
Object localObject = lockObject3Inflated;
synchronized (localObject) {
for (int i = 0; i < innerCount; i++) {
synchronized (localObject) {
dummyInt1++;
dummyInt2++;
}
}
}
}
/** Perform a recursive-only synchronized on an object within a loop. */
@Benchmark
public void testInflatedRecursiveOnlyLockUnlock() {
synchronized (lockObject3Inflated) {
for (int i = 0; i < innerCount; i++) {
synchronized (lockObject3Inflated) {
dummyInt1++;
dummyInt2++;
}
}
}
}
/** Perform a recursive-only synchronized on a local object within a loop. */
@Benchmark
public void testInflatedRecursiveLockUnlockLocal() {
Object localObject = lockObject3Inflated;
for (int i = 0; i < innerCount; i++) {
synchronized (localObject) {
synchronized (localObject) {
dummyInt1++;
dummyInt2++;
}
}
}
}
/** Perform a recursive-only synchronized on an object within a loop. */
@Benchmark
public void testInflatedRecursiveLockUnlock() {
for (int i = 0; i < innerCount; i++) {
synchronized (lockObject3Inflated) {
synchronized (lockObject3Inflated) {
dummyInt1++;
dummyInt2++;
}
}
}
}
/**
* Performs recursive synchronizations on the same local object.
* <p/>