On Tue, 13 Aug 2024 14:52:03 GMT, Axel Boldt-Christmas <[email protected]>
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 two
> additional commits since the last revision:
>
> - Remove the last OMWorld references
> - Rename omworldtable_work to object_monitor_table_work
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,
nit typo: s/does not/do not/
src/hotspot/share/runtime/basicLock.inline.hpp line 54:
> 52: inline void BasicLock::clear_object_monitor_cache() {
> 53: assert(UseObjectMonitorTable, "must be");
> 54: set_metadata(0);
Should this be a literal `0` or should it be `nullptr`?
Update: The metadata field is of type `unintptr_t`. Got it.
src/hotspot/share/runtime/deoptimization.cpp line 1650:
> 1648: mon_info->lock()->set_bad_metadata_deopt();
> 1649: }
> 1650: #endif
I like this!
src/hotspot/share/runtime/globals.hpp line 1964:
> 1962:
> \
> 1963: product(int, LightweightFastLockingSpins, 13, DIAGNOSTIC,
> \
> 1964: "Specifies the number of time lightweight fast locking will "
> \
nit typo: s/number of time/number of times/
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 34:
> 32: #include "oops/oop.inline.hpp"
> 33: #include "runtime/atomic.hpp"
> 34: #include "memory/allStatic.hpp"
nit: this include is out of order.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 43:
> 41: #include "runtime/mutexLocker.hpp"
> 42: #include "runtime/objectMonitor.hpp"
> 43: #include "runtime/objectMonitor.inline.hpp"
Shouldn't have both includes here.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 81:
> 79: oop _obj;
> 80:
> 81: public:
nit - please indent by one more space.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 86:
> 84: uintx get_hash() const {
> 85: uintx hash = _obj->mark().hash();
> 86: assert(hash != 0, "should have a hash");
Hmmm... I can remember seeing hash values of zero in some
of my older legacy inflation stress runs. Is a hash value of zero
not a thing with lightweight locking?
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 104:
> 102: ObjectMonitor* _monitor;
> 103:
> 104: public:
nit - please indent by one more space.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 126:
> 124:
> 125: static void dec_items_count() {
> 126: Atomic::inc(&_items_count);
Shouldn't this be `Atomic::dec`?
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 130:
> 128:
> 129: static double get_load_factor() {
> 130: return (double)_items_count/(double)_table_size;
nit - please add spaces around `/` operator.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 169:
> 167: }
> 168:
> 169: public:
nit - please indent by one space.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 184:
> 182: bool has_monitor = obj->mark().has_monitor();
> 183: assert(has_monitor == (monitor != nullptr),
> 184: "Inconsistency between markWord and OMW table has_monitor: %s
> monitor: " PTR_FORMAT,
Do you still want to use the name "OMW table"?
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 213:
> 211:
> 212: static bool should_shrink() {
> 213: // No implemented;
nit typo: s/No/Not/
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 322:
> 320: oop obj = om->object_peek();
> 321: st->print("monitor " PTR_FORMAT " ", p2i(om));
> 322: st->print("object " PTR_FORMAT, p2i(obj));
The monitor output style is to use `=` and commas, e.g.
`monitor=<value>, object=<value>`
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 341:
> 339:
> 340: ObjectMonitor*
> LightweightSynchronizer::get_or_insert_monitor_from_table(oop object,
> JavaThread* current, bool* inserted) {
> 341: assert(LockingMode == LM_LIGHTWEIGHT, "must be");
Do you want to assert: `inserted != nullptr`?
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 367:
> 365: ResourceMark rm(current);
> 366: log_trace(monitorinflation)("inflate: object=" INTPTR_FORMAT ",
> mark="
> 367: INTPTR_FORMAT ", type='%s' cause %s",
> p2i(object),
nit typo: s/cause %s/cause=%s/
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 414:
> 412:
> 413: intptr_t hash = obj->mark().hash();
> 414: assert(hash != 0, "must be set when claiming the object monitor");
Hmmm... I can remember seeing hash values of zero in some
of my older legacy inflation stress runs. Is a hash value of zero
not a thing with lightweight locking?
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 468:
> 466: oop obj = *o;
> 467: if (obj->mark_acquire().has_monitor()) {
> 468: if (_length > 0 && _contended_oops[_length-1] == obj) {
nit - please add space around `-` operator.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 501:
> 499: // Make room on lock_stack
> 500: if (lock_stack.is_full()) {
> 501: // Inflate contented objects
nit typo: s/contented/contended/
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 545:
> 543: bool _no_safepoint;
> 544:
> 545: public:
nit - please indent by one space.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 664:
> 662:
> 663: // Used when deflation is observed. Progress here requires progress
> 664: // from the deflator. After observing the that the deflator is not
nit typo: s/observing the that the deflator/observing that the deflator/
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 760:
> 758:
> 759: // LightweightSynchronizer::inflate_locked_or_imse is used to to get an
> inflated
> 760: // ObjectMonitor* with LM_LIGHTWEIGHT. It is used from contexts which
> requires
nit typo: s/used from contexts which requires/used from contexts which require/
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 773:
> 771: JavaThread* current = THREAD;
> 772:
> 773: for(;;) {
nit: please add space before `(`.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 778:
> 776: // No lock, IMSE.
> 777: THROW_MSG_(vmSymbols::java_lang_IllegalMonitorStateException(),
> 778: "current thread is not owner", nullptr);
nit - please indent by one more space.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 785:
> 783: // Fast locked by other thread, IMSE.
> 784: THROW_MSG_(vmSymbols::java_lang_IllegalMonitorStateException(),
> 785: "current thread is not owner", nullptr);
nit - please indent by one more space.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 799:
> 797: if (lock_stack.contains(obj)) {
> 798: // Current thread owns the lock but someone else inflated
> 799: // fix owner and pop lock stack
Please consider:
// Current thread owns the lock but someone else inflated it.
// Fix owner and pop lock stack.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 805:
> 803: // Fast locked (and inflated) by other thread, or deflation in
> progress, IMSE.
> 804: THROW_MSG_(vmSymbols::java_lang_IllegalMonitorStateException(),
> 805: "current thread is not owner", nullptr);
nit - please indent by one more space.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 1045:
> 1043: if (monitor->is_being_async_deflated()) {
> 1044: // The MonitorDeflation thread is deflating the monitor. The
> locking thread
> 1045: // must spin until further progress have been made.
nit typo: s/progress have been made./progress has been made./
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 1075:
> 1073: // lock, then we make the locking_thread thread
> 1074: // the ObjectMonitor owner and remove the
> 1075: // lock from the locking_thread thread's lock
> stack.
nit typos: s/locking_thread thread/locking_thread/
in three places.
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 1190:
> 1188:
> 1189: #ifndef _LP64
> 1190: // Only for 32bit which have limited support for fast locking outside
> the runtime.
nit typo: s/which have limited support/which has limited support/
src/hotspot/share/runtime/lightweightSynchronizer.cpp line 1194:
> 1192: // Recursive lock successful.
> 1193: current->inc_held_monitor_count();
> 1194: // Clears object monitor cache, because ?
What does this comment mean?
src/hotspot/share/runtime/lightweightSynchronizer.hpp line 36:
> 34:
> 35: class LightweightSynchronizer : AllStatic {
> 36: private:
nit: please indent by 1 space.
src/hotspot/share/runtime/lightweightSynchronizer.hpp line 41:
> 39:
> 40: static ObjectMonitor* add_monitor(JavaThread* current, ObjectMonitor*
> monitor, oop obj);
> 41: static bool remove_monitor(Thread* current, oop obj, ObjectMonitor*
> monitor);
Hmmm... `add_monitor` has `monitor` and then `obj` params.
`remove_monitor` has `obj` and then `monitor` params. Why
not use the same order?
src/hotspot/share/runtime/lightweightSynchronizer.hpp line 57:
> 55: static bool resize_table(JavaThread* current);
> 56:
> 57: private:
nit: please indent by 1 space.
src/hotspot/share/runtime/lightweightSynchronizer.hpp line 61:
> 59: static bool fast_lock_spin_enter(oop obj, LockStack& lock_stack,
> JavaThread* current, bool observed_deflation);
> 60:
> 61: public:
nit: please indent by 1 space.
src/hotspot/share/runtime/lightweightSynchronizer.hpp line 69:
> 67: static ObjectMonitor* inflate_locked_or_imse(oop object,
> ObjectSynchronizer::InflateCause cause, TRAPS);
> 68: static ObjectMonitor* inflate_fast_locked_object(oop object,
> JavaThread* locking_thread, JavaThread* current,
> ObjectSynchronizer::InflateCause cause);
> 69: static ObjectMonitor* inflate_and_enter(oop object, JavaThread*
> locking_thread, JavaThread* current, ObjectSynchronizer::InflateCause cause);
All of these are "inflate" functions, but:
- two of them have `object` parameter next to the `cause` parameter
- two of them have `object` parameter first
- one of them has `current` parameter before the other thread parameter
(`inflating_thread`)
- two of them have the `current` parameter after the other thread parameter
(`locking_thread`)
Please consider making the parameter order consistent.
src/hotspot/share/runtime/lockStack.hpp line 130:
> 128: class OMCache {
> 129: friend class VMStructs;
> 130: public:
Please indent by one space.
src/hotspot/share/runtime/lockStack.hpp line 133:
> 131: static constexpr int CAPACITY = 8;
> 132:
> 133: private:
Please indent by one space.
src/hotspot/share/runtime/lockStack.hpp line 140:
> 138: const oop _null_sentinel = nullptr;
> 139:
> 140: public:
Please indent by one space.
src/hotspot/share/runtime/objectMonitor.cpp line 669:
> 667: install_displaced_markword_in_object(obj);
> 668: }
> 669: }
This can be cleaned up by putting L664 and L665 on the same line,
fixing the indents on L666-7 and deleting L668.
src/hotspot/share/runtime/objectMonitor.cpp line 682:
> 680: // deflation process.
> 681: void ObjectMonitor::install_displaced_markword_in_object(const oop obj) {
> 682: assert(!UseObjectMonitorTable, "Lightweight has no dmw");
Perhaps:
`assert(!UseObjectMonitorTable, "ObjectMonitorTable has no dmw");`
src/hotspot/share/runtime/objectMonitor.hpp line 388:
> 386: // Deflation support
> 387: bool deflate_monitor(Thread* current);
> 388: private:
nit - please indent by one space
src/hotspot/share/runtime/serviceThread.cpp line 44:
> 42: #include "runtime/jniHandles.hpp"
> 43: #include "runtime/serviceThread.hpp"
> 44: #include "runtime/lightweightSynchronizer.hpp"
Include is in the wrong place.
src/hotspot/share/runtime/synchronizer.cpp line 404:
> 402:
> 403: bool ObjectSynchronizer::quick_enter_legacy(oop obj, JavaThread* current,
> 404: BasicLock * lock) {
nit - please fix this indent.
-------------
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715569720
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715571831
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715582675
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715589490
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715617774
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715618379
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715621030
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715623399
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715640861
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715642307
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715643617
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715647133
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715649276
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715651080
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715661781
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715663965
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715662800
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715675411
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715678877
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715680995
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715682621
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715690997
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715696004
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715697549
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715698164
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715698568
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715700114
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715700801
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715716901
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715720857
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715724929
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715726115
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715603974
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715601643
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715604333
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715604553
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715614530
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715880925
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715881116
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715881379
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715922871
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715924876
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715900092
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715926418
PR Review Comment: https://git.openjdk.org/jdk/pull/20067#discussion_r1715940064