8319773: Avoid inflating monitors when installing hash codes for LM_LIGHTWEIGHT

Reviewed-by: rkennke, dcubed, thartmann
This commit is contained in:
Axel Boldt-Christmas 2024-01-12 14:34:42 +00:00
parent e22ab10991
commit 65a0672791
4 changed files with 53 additions and 47 deletions

View File

@ -26,6 +26,7 @@
#include "asm/macroAssembler.hpp"
#include "runtime/interfaceSupport.inline.hpp"
#include "runtime/sharedRuntime.hpp"
#include "utilities/globalDefinitions.hpp"
#include "vmreg_x86.inline.hpp"
#ifdef COMPILER1
#include "c1/c1_Runtime1.hpp"
@ -59,9 +60,16 @@ void SharedRuntime::inline_check_hashcode_from_object_header(MacroAssembler* mas
__ movptr(result, Address(obj_reg, oopDesc::mark_offset_in_bytes()));
// check if locked
__ testptr(result, markWord::unlocked_value);
__ jcc(Assembler::zero, slowCase);
if (LockingMode == LM_LIGHTWEIGHT) {
// check if monitor
__ testptr(result, markWord::monitor_value);
__ jcc(Assembler::notZero, slowCase);
} else {
// check if locked
__ testptr(result, markWord::unlocked_value);
__ jcc(Assembler::zero, slowCase);
}
// get hash
#ifdef _LP64

View File

@ -4572,14 +4572,22 @@ 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 unlocked.
// 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));
Node *unlocked_val = _gvn.MakeConX(markWord::unlocked_value);
Node *chk_unlocked = _gvn.transform(new CmpXNode( lmasked_header, unlocked_val));
Node *test_unlocked = _gvn.transform(new BoolNode( chk_unlocked, BoolTest::ne));
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_unlocked, slow_region);
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);
}
// Get the hash value and check to see that it has been properly assigned.
// We depend on hash_mask being at most 32 bits and avoid the use of

View File

@ -519,16 +519,18 @@ void ObjectSynchronizer::enter(Handle obj, BasicLock* lock, JavaThread* current)
LockStack& lock_stack = current->lock_stack();
if (lock_stack.can_push()) {
markWord mark = obj()->mark_acquire();
if (mark.is_neutral()) {
assert(!lock_stack.contains(obj()), "thread must not already hold the lock");
while (mark.is_neutral()) {
// 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.
markWord locked_mark = mark.set_fast_locked();
markWord old_mark = obj()->cas_set_mark(locked_mark, mark);
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;
}
mark = old_mark;
}
}
// All other paths fall-through to inflate-enter.
@ -578,23 +580,15 @@ void ObjectSynchronizer::exit(oop object, BasicLock* lock, JavaThread* current)
markWord mark = object->mark();
if (LockingMode == LM_LIGHTWEIGHT) {
// Fast-locking does not use the 'lock' argument.
if (mark.is_fast_locked()) {
markWord unlocked_mark = mark.set_unlocked();
markWord old_mark = object->cas_set_mark(unlocked_mark, mark);
if (old_mark != mark) {
// Another thread won the CAS, it must have inflated the monitor.
// It can only have installed an anonymously locked monitor at this point.
// Fetch that monitor, set owner correctly to this thread, and
// exit it (allowing waiting threads to enter).
assert(old_mark.has_monitor(), "must have monitor");
ObjectMonitor* monitor = old_mark.monitor();
assert(monitor->is_owner_anonymous(), "must be anonymous owner");
monitor->set_owner_from_anonymous(current);
monitor->exit(current);
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) {
current->lock_stack().remove(object);
return;
}
LockStack& lock_stack = current->lock_stack();
lock_stack.remove(object);
return;
mark = old_mark;
}
} else if (LockingMode == LM_LEGACY) {
markWord dhw = lock->displaced_header();
@ -908,13 +902,6 @@ static inline intptr_t get_next_hash(Thread* current, oop obj) {
return value;
}
// Can be called from non JavaThreads (e.g., VMThread) for FastHashCode
// calculations as part of JVM/TI tagging.
static bool is_lock_owned(Thread* thread, oop obj) {
assert(LockingMode == LM_LIGHTWEIGHT, "only call this with new lightweight locking enabled");
return thread->is_Java_thread() ? JavaThread::cast(thread)->lock_stack().contains(obj) : false;
}
intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
while (true) {
@ -926,7 +913,7 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
assert(LockingMode == LM_MONITOR, "+VerifyHeavyMonitors requires LockingMode == 0 (LM_MONITOR)");
guarantee((obj->mark().value() & markWord::lock_mask_in_place) != markWord::locked_value, "must not be lightweight/stack-locked");
}
if (mark.is_neutral()) { // if this is a normal header
if (mark.is_neutral() || (LockingMode == LM_LIGHTWEIGHT && mark.is_fast_locked())) {
hash = mark.hash();
if (hash != 0) { // if it has a hash, just return it
return hash;
@ -938,6 +925,10 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
if (test == mark) { // if the hash was installed, return it
return hash;
}
if (LockingMode == LM_LIGHTWEIGHT) {
// CAS failed, retry
continue;
}
// Failed to install the hash. It could be that another thread
// installed the hash just before our attempt or inflation has
// occurred or... so we fall thru to inflate the monitor for
@ -969,13 +960,6 @@ intptr_t ObjectSynchronizer::FastHashCode(Thread* current, oop obj) {
}
// Fall thru so we only have one place that installs the hash in
// the ObjectMonitor.
} else if (LockingMode == LM_LIGHTWEIGHT && mark.is_fast_locked() && is_lock_owned(current, obj)) {
// This is a fast-lock owned by the calling thread so use the
// markWord from the object.
hash = mark.hash();
if (hash != 0) { // if it has a hash, just return it
return hash;
}
} else if (LockingMode == LM_LEGACY && mark.has_locker() && current->is_lock_owned((address)mark.locker())) {
// This is a stack-lock owned by the calling thread so fetch the
// displaced markWord from the BasicLock on the stack.
@ -1305,6 +1289,13 @@ void ObjectSynchronizer::inflate_helper(oop obj) {
(void)inflate(Thread::current(), obj, inflate_cause_vm_internal);
}
// Can be called from non JavaThreads (e.g., VMThread) for FastHashCode
// calculations as part of JVM/TI tagging.
static bool is_lock_owned(Thread* thread, oop obj) {
assert(LockingMode == LM_LIGHTWEIGHT, "only call this with new lightweight locking enabled");
return thread->is_Java_thread() ? JavaThread::cast(thread)->lock_stack().contains(obj) : false;
}
ObjectMonitor* ObjectSynchronizer::inflate(Thread* current, oop object,
const InflateCause cause) {
EventJavaMonitorInflate event;

View File

@ -61,12 +61,11 @@ public class TestWBDeflateIdleMonitors {
static WhiteBox wb = WhiteBox.getWhiteBox();
public static Object obj;
public static void main(String args[]) {
public static void main(String args[]) throws Exception {
obj = new Object();
synchronized (obj) {
// HotSpot implementation detail: asking for the hash code
// when the object is locked causes monitor inflation.
if (obj.hashCode() == 0xBAD) System.out.println("!");
// The current implementation of notify-wait requires inflation.
obj.wait(1);
Asserts.assertEQ(wb.isMonitorInflated(obj), true,
"Monitor should be inflated.");
}