On Fri, 12 Jul 2024 05:57:30 GMT, Axel Boldt-Christmas <abold...@openjdk.org> wrote:
>> When inflating a monitor the `ObjectMonitor*` is written directly over the >> `markWord` and any overwritten data is displaced into a displaced >> `markWord`. This is problematic for concurrent GCs which needs extra care or >> looser semantics to use this displaced data. In Lilliput this data also >> contains the klass forcing this to be something that the GC has to take into >> account everywhere. >> >> This patch introduces an alternative solution where locking only uses the >> lock bits of the `markWord` and inflation does not override and displace the >> `markWord`. This is done by keeping associations between objects and >> `ObjectMonitor*` in an external hash table. Different caching techniques are >> used to speedup lookups from compiled code. >> >> A diagnostic VM option is introduced called `UseObjectMonitorTable`. It is >> only supported in combination with the LM_LIGHTWEIGHT locking mode (the >> default). >> >> This patch has been evaluated to be performance neutral when >> `UseObjectMonitorTable` is turned off (the default). >> >> Below is a more detailed explanation of this change and how `LM_LIGHTWEIGHT` >> and `UseObjectMonitorTable` works. >> >> # Cleanups >> >> Cleaned up displaced header usage for: >> * BasicLock >> * Contains some Zero changes >> * Renames one exported JVMCI field >> * ObjectMonitor >> * Updates comments and tests consistencies >> >> # Refactoring >> >> `ObjectMonitor::enter` has been refactored an a >> `ObjectMonitorContentionMark` witness object has been introduced to the >> signatures. Which signals that the contentions reference counter is being >> held. More details are given below in the section about deflation. >> >> The initial purpose of this was to allow `UseObjectMonitorTable` to interact >> more seamlessly with the `ObjectMonitor::enter` code. >> >> _There is even more `ObjectMonitor` refactoring which can be done here to >> create a more understandable and enforceable API. There are a handful of >> invariants / assumptions which are not always explicitly asserted which >> could be trivially abstracted and verified by the type system by using >> similar witness objects._ >> >> # LightweightSynchronizer >> >> Working on adapting and incorporating the following section as a comment in >> the source code >> >> ## Fast Locking >> >> CAS on locking bits in markWord. >> 0b00 (Fast Locked) <--> 0b01 (Unlocked) >> >> When locking and 0b00 (Fast Locked) is observed, it may be beneficial to >> avoid inflating by spinning a bit. >> >> If 0b10 (Inflated) is observed or there is to... > > Axel Boldt-Christmas has updated the pull request incrementally with one > additional commit since the last revision: > > Update arguments.cpp Here comes my first-pass review of the shared code. (Man, I hope we can get rid of UOMT soon, again...) src/hotspot/share/oops/instanceKlass.cpp line 1090: > 1088: > 1089: // Step 2 > 1090: // If we were to use wait() instead of waitUninterruptibly() then This is a nice correction (even though, the actual call below is wait_uninterruptibly() ;-) ), but seems totally unrelated. src/hotspot/share/oops/markWord.cpp line 27: > 25: #include "precompiled.hpp" > 26: #include "oops/markWord.hpp" > 27: #include "runtime/basicLock.inline.hpp" I don't think this include is needed (at least not by the changed code parts, I haven't checked existing code). src/hotspot/share/runtime/arguments.cpp line 1820: > 1818: warning("New lightweight locking not supported on this platform"); > 1819: } > 1820: if (UseObjectMonitorTable) { Uhm, wait a second. That list of platforms covers all existing platforms anyway, so the whole block could be removed? Or is there a deeper meaning here that I don't understand? src/hotspot/share/runtime/basicLock.cpp line 37: > 35: if (mon != nullptr) { > 36: mon->print_on(st); > 37: } I am not sure if we wanted to do this, but we know the owner, therefore we could also look-up the OM from the table, and print it. It wouldn't have all that much to do with the BasicLock, though. src/hotspot/share/runtime/basicLock.inline.hpp line 45: > 43: return reinterpret_cast<ObjectMonitor*>(get_metadata()); > 44: #else > 45: // Other platforms does not make use of the cache yet, If it's not used, why does it matter to special case the code here? src/hotspot/share/runtime/lightweightSynchronizer.cpp line 28: > 26: > 27: #include "classfile/vmSymbols.hpp" > 28: #include "javaThread.inline.hpp" This include is incorrect (and my IDE says it's not needed). src/hotspot/share/runtime/lightweightSynchronizer.cpp line 31: > 29: #include "jfrfiles/jfrEventClasses.hpp" > 30: #include "logging/log.hpp" > 31: #include "logging/logStream.hpp" Include of logStream.hpp not needed? src/hotspot/share/runtime/lightweightSynchronizer.cpp line 58: > 56: > 57: // > 58: // Lightweight synchronization. This comment doesn't really say anything. Either remove it, or add a nice summary of how LW locking and OM table stuff works. src/hotspot/share/runtime/lightweightSynchronizer.cpp line 80: > 78: > 79: ConcurrentTable* _table; > 80: volatile size_t _table_count; Looks like a misnomer to me. We only have one table, but we do have N entries/nodes. This is counted when new nodes are allocated or old nodes are freed. Consider renaming this to '_entry_count' or '_node_count'? I'm actually a bit surprised if ConcurrentHashTable doesn't already track this... src/hotspot/share/runtime/lightweightSynchronizer.cpp line 88: > 86: > 87: public: > 88: Lookup(oop obj) : _obj(obj) {} Make explicit? src/hotspot/share/runtime/lightweightSynchronizer.cpp line 97: > 95: > 96: bool equals(ObjectMonitor** value) { > 97: // The entry is going to be removed soon. What does this comment mean? src/hotspot/share/runtime/lightweightSynchronizer.cpp line 112: > 110: > 111: public: > 112: LookupMonitor(ObjectMonitor* monitor) : _monitor(monitor) {} Make explicit? src/hotspot/share/runtime/lightweightSynchronizer.cpp line 159: > 157: static size_t min_log_size() { > 158: // ~= log(AvgMonitorsPerThreadEstimate default) > 159: return 10; Uh wait - are we assuming that threads hold 1024 monitors *on average* ? Isn't this a bit excessive? I would have thought maybe 8 monitors/thread. Yes there are workloads that are bonkers. Or maybe the comment/flag name does not say what I think it says. Or why not use AvgMonitorsPerThreadEstimate directly? src/hotspot/share/runtime/lightweightSynchronizer.cpp line 349: > 347: assert(LockingMode == LM_LIGHTWEIGHT, "must be"); > 348: > 349: if (try_read) { All the callers seem to pass try_read = true. Why do we have the branch at all? src/hotspot/share/runtime/lightweightSynchronizer.cpp line 401: > 399: > 400: if (inserted) { > 401: // Hopefully the performance counters are allocated on distinct It doesn't look like the counters are on distinct cache lines (see objectMonitor.hpp, lines 212ff). If this is a concern, file a bug to investigate it later? The comment here is a bit misplaced, IMO. src/hotspot/share/runtime/lightweightSynchronizer.cpp line 473: > 471: int _length; > 472: > 473: void do_oop(oop* o) final { C++ always provides something to learn - C++ has got a final keyword! :-) Looks like a reasonable use of it here, though. src/hotspot/share/runtime/lightweightSynchronizer.cpp line 477: > 475: if (obj->mark_acquire().has_monitor()) { > 476: if (_length > 0 && _contended_oops[_length-1] == obj) { > 477: // assert(VM_Version::supports_recursive_lightweight_locking(), > "must be"); Uncomment or remove assert? src/hotspot/share/runtime/lightweightSynchronizer.cpp line 554: > 552: bool _no_safepoint; > 553: union { > 554: struct {} _dummy; Uhh ... Why does this need to be wrapped in a union and struct? src/hotspot/share/runtime/lightweightSynchronizer.cpp line 563: > 561: assert(locking_thread == current || > locking_thread->is_obj_deopt_suspend(), "locking_thread may not run > concurrently"); > 562: if (_no_safepoint) { > 563: ::new (&_nsv) NoSafepointVerifier(); I'm thinking that it might be easier and cleaner to just re-do what the NoSafepointVerifier does? It just calls thread->inc/dec _no_safepoint_count(). src/hotspot/share/runtime/lightweightSynchronizer.cpp line 748: > 746: } > 747: > 748: // Fast-locking does not use the 'lock' argument. I believe the comment is outdated. src/hotspot/share/runtime/lightweightSynchronizer.cpp line 969: > 967: > 968: for (;;) { > 969: // Fetch the monitor from the table Wrong intendation. src/hotspot/share/runtime/lightweightSynchronizer.cpp line 1157: > 1155: // enter can block for safepoints; clear the unhandled object oop > 1156: PauseNoSafepointVerifier pnsv(&nsv); > 1157: object = nullptr; What is the point of that statement? object is not an out-arg (afaict), and not used subsequently. src/hotspot/share/runtime/lightweightSynchronizer.hpp line 68: > 66: static void exit(oop object, JavaThread* current); > 67: > 68: static ObjectMonitor* inflate_into_object_header(Thread* current, > JavaThread* inflating_thread, oop object, const > ObjectSynchronizer::InflateCause cause); My IDE flags this with a warning 'Parameter 'cause' is const-qualified in the function declaration; const-qualification of parameters only has an effect in function definitions' *shrugs* src/hotspot/share/runtime/lockStack.inline.hpp line 232: > 230: oop obj = monitor->object_peek(); > 231: assert(obj != nullptr, "must be alive"); > 232: assert(monitor == > LightweightSynchronizer::get_monitor_from_table(JavaThread::current(), obj), > "must be exist in table"); "must be exist in table" -> "must exist in table" src/hotspot/share/runtime/objectMonitor.cpp line 56: > 54: #include "runtime/safepointMechanism.inline.hpp" > 55: #include "runtime/sharedRuntime.hpp" > 56: #include "runtime/synchronizer.hpp" This include is not used. src/hotspot/share/runtime/objectMonitor.hpp line 193: > 191: ObjectWaiter* volatile _WaitSet; // LL of threads wait()ing on the > monitor > 192: volatile int _waiters; // number of waiting threads > 193: private: You can now also remove the 'private:' here src/hotspot/share/runtime/synchronizer.cpp line 390: > 388: > 389: static bool useHeavyMonitors() { > 390: #if defined(X86) || defined(AARCH64) || defined(PPC64) || > defined(RISCV64) || defined(S390) Why are those if-defs here? Why is ARM excluded? ------------- Changes requested by rkennke (Reviewer). PR Review: https://git.openjdk.org/jdk/pull/20067#pullrequestreview-2174478048 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675695457 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675696406 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675704824 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675707735 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675711809 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675744474 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675745048 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1676111067 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675773683 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675747483 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675765460 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675766088 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675781420 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675791687 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675799897 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675803217 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675805690 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675824394 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675832868 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675854207 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675876915 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675932005 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1675936943 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1676107048 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1676112375 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1676125325 PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1676140201