Monitor concept
Java object header
Take 32-bit virtual machine as an example
Common object
Array object
The Mark Word structure is
64 bit virtual machine Mark Word
reference material
https://stackoverflow.com/questions/26357186/what-is-in-java-object-header
Principle of Monitor (lock)
Monitor is translated into monitor or tube
Each Java object can be associated with a Monitor object. If you use synchronized to lock the object (heavyweight), the pointer to the Monitor object will be set in the Mark Work of the object header
The Monitor structure is as follows
- At the beginning, the Owner in Monitor is null
- When Thread-2 executes synchronized(obj), the Monitor Owner will be set to Thread-2. There can only be one Owner in the Monitor
- In the process of locking Thread-2, if Thread-3, Thread-4 and Thread-5 also execute synchronized(obj), it will be included in EntryList BLOCKED
- Thread-2 executes the contents of the synchronization code block, and then wakes up the waiting thread in the EntryList to compete for the lock. The competition is unfair
- On the way, Thread-0 and Thread-1 in the WaitSet are threads that have previously obtained locks and entered the WAITING state if the single condition is not met. They will be analyzed later when we talk about wait notify
be careful
- synchronized must enter the Monitor of the same object to have the above effect
- Objects that are not synchronized are not associated with monitors and do not comply with the above rules
synchronized principle
static final Object lock = new Object(); static int counter = 0; public static void main(String[] args) { synchronized (lock) { counter++; } }
The corresponding bytecode is
public static void main(java.lang.String[]); descriptor: ([Ljava/lang/String;)V flags: ACC_PUBLIC, ACC_STATIC Code: stack=2, locals=3, args_size=1 0: getstatic #Synchronize / < / - synchronize 2 3: dup 4: astore_1 // lock reference - > slot 1 5: monitorenter // Set the lock object MarkWord as the Monitor pointer 6: getstatic #3 // <- i 9: iconst_1 // Preparation constant 1 10: iadd // +1 11: putstatic #3 // -> i 14: aload_1 // < - lock reference 15: monitorexit // Reset the lock object MarkWord and wake up the EntryList 16: goto 24 19: astore_2 // e -> slot 2 20: aload_1 // < - lock reference 21: monitorexit // Reset the lock object MarkWord and wake up the EntryList 22: aload_2 // <- slot 2 (e) 23: athrow // throw e 24: return Exception table: from to target type 6 16 19 any 19 22 19 any LineNumberTable: line 8: 0 line 9: 6 line 10: 14 line 11: 24 LocalVariableTable: Start Length Slot Name Signature 0 25 0 args [Ljava/lang/String; StackMapTable: number_of_entries = 2 frame_type = 255 /* full_frame */ offset_delta = 19 locals = [ class "[Ljava/lang/String;", class java/lang/Object ] stack = [ class java/lang/Throwable ] frame_type = 250 /* chop */ offset_delta = 4
be careful
Method level synchronized is no longer directly reflected in the command
Advanced principle of synchronized
1. Lightweight lock
Scenario of using lightweight lock: if an object has multiple threads to lock, but the locking time is staggered (that is, there is no competition), then lightweight lock can be used to optimize.
Lightweight locks are transparent to users, and the syntax is still synchronized
Suppose there are two methods to synchronize blocks and lock the same object
static final Object obj = new Object(); public static void method1() { synchronized( obj ) { // Synchronization block A method2(); } } public static void method2() { synchronized( obj ) { // Synchronization block B } }
- Create a Lock Record object. Each thread will contain a Lock Record structure, which can store the Mark Word of the locked object
- Let the Object reference in the lock record point to the lock Object, try to replace the Mark Word in the Object with cas, and store the value of Mark Word in the lock record
- If the cas replacement is successful, the lock record address and status 00 are stored in the object header, indicating that the thread locks the object. At this time, the figure is as follows
- If cas fails, there are two situations
- If another thread already holds the lightweight lock of the Object, it indicates that there is competition and enters the lock inflation process
- If synchronized lock reentry is performed by yourself, add another Lock Record as the count of reentry
- When exiting the synchronized code block (when unlocking), if there is a lock record with a value of null, it indicates that there is reentry and the reentry count is reduced by one
- When exiting the synchronized code block (when unlocking), the value of the lock record is filled with null. At this time, cas is used to reply the value of Mark Word to the object header
- If successful, the unlocking is successful
- Failure indicates that the lightweight lock has undergone lock expansion or has been upgraded to a heavyweight lock, and enters the heavyweight lock unlocking process
2. Lock expansion
If cas fails in the process of trying to add a lightweight lock, then another thread adds a heavyweight lock (with competition) to this object. At this time, lock expansion is required to change the lightweight lock into a heavyweight lock
tatic Object obj = new Object(); public static void method1() { synchronized( obj ) { // Synchronization block } }
-
When Thread-1 locks with a lightweight lock, Thread-0 has already locked the object with a lightweight lock
-
When Thread-1 fails to add a lightweight lock, enter the lock expansion process
- That is, apply for the Monitor lock for the Object object and let the Object point to the heavyweight lock address
- Then enter the EntryList BLOCKED of Monitor
-
When Thread-0 exits synchronization and the synchronization block is unlocked, cas is used to restore the value of Mark Word to the object header, which fails. At this time, the heavyweight lock process will enter, that is, find the Monitor object according to the Monitor address, set the Owner to null, and wake up the bolded thread in the EntryList
3. Spin optimization
When competing for heavyweight locks, you can also use spin to optimize. If the current thread spins successfully (that is, the lock holding thread has exited the synchronization block and released the lock), the current thread can avoid blocking.
Spin retry success
Spin retry failure
- Spin will occupy CPU time. Single core CPU spin is a waste, and multi-core CPU spin can give full play to its advantages
- After Java 6, the spin lock is adaptive. For example, if the object has just succeeded in one spin, it is considered that the possibility of successful spin this time will be high, so spin more times; On the contrary, less spin or even no spin. In short, it is more intelligent
- After Java 7, you can't control whether to turn on the spin function
4. Bias lock
When the lightweight lock has no competition (only its own thread), it still needs to perform cas operation every time it re enters.
Java 6 introduces biased locking for further optimization: only when the cas thread id is set to the Mark Word header of the object for the first time, and then it is found that the thread id is itself, it means that there is no competition and there is no need to re cas. After that, as long as there is no competition, the object belongs to the thread
For example:
static final Object obj = new Object(); public static void m1() { synchronized( obj ) { // Synchronization block A m2(); } } public static void m2() { synchronized( obj ) { // Synchronization block B m3(); } } public static void m3() { synchronized( obj ) { // Synchronization block C } }
Biased state
Recall the object header format
When an object is created:
- If the bias lock is enabled (it is enabled by default), after the object is created, the value of Mark Word is 0x05, that is, the last three digits are 101. At this time, its thread, epoch and age are all 0
- Bias lock is the default delay and will not take effect immediately when the program starts. If you want to avoid the delay, you can add VM parameter - XX:BiasedLockingStartupDelay=0 to disable the delay
- If the bias lock is not opened, the value of Mark Word is 0x01 after the object is created, that is, the last three digits are 001. At this time, its hashcode and age are all 0. It will be assigned when hashcode is used for the first time
1) Test delay characteristics
Dog d = new Dog(); ClassLayout classLayout = ClassLayout.parseInstance(d); log.debug("Mark Word before"); System.out.println(classLayout.toPrintable(d)); try { Thread.sleep(4000); } catch (InterruptedException e) { e.printStackTrace(); } log.debug("Mark Word before"); System.out.println(classLayout.toPrintable(new Dog()));
output
2022/03/05-23:52:50.383 [main] c.Dog - synchronized before sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/05-23:52:54.383 [main] c.Dog - synchronized after sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
The object created before the skew lock delay start ends with Mark Work or 01, and the object created after that ends with 101.
It should be noted that due to the problem of storing data at the size end of the operating system, the location that needs attention is shown in the figure
Large and small end storage
- Large end (storage) mode: it means that the contents of the low byte order of a data are placed at the high address, and the contents of the high byte order are placed at the low address.
- Small end (storage) mode: it means that the low byte order content of a data is stored at the low address and the high byte order content is stored at the high address. (it can be summarized as "small small", i.e. low order, low address and small end)
If the VM parameter - XX:BiasedLockingStartupDelay=0 is used, the bias lock can be turned off to delay startup. The code is as above and output
2022/03/06-00:28:45.626 [main] c.Dog - Mark Word before sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 2d 6b 01 f8 (00101101 01101011 00000001 11111000) (-134124755) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-00:28:49.636 [main] c.Dog - Mark Word before sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) 2d 6b 01 f8 (00101101 01101011 00000001 11111000) (-134124755) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
2) Test deflection lock
Dog d = new Dog(); ClassLayout classLayout = ClassLayout.parseInstance(d); new Thread(() -> { log.debug("synchronized before"); System.out.println(classLayout.toPrintable(d)); synchronized (d) { log.debug("synchronized in"); System.out.println(classLayout.toPrintable(d)); } log.debug("synchronized after"); System.out.println(classLayout.toPrintable(d)); }, "t1").start();
output
sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-00:31:49.943 [t1] c.Dog - synchronized in sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 40 3a 1f (00000101 01000000 00111010 00011111) (523911173) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-00:31:49.943 [t1] c.Dog - synchronized after sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 40 3a 1f (00000101 01000000 00111010 00011111) (523911173) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
be careful
After the object in the bias lock is unlocked, the thread id is still stored in the object header
3) Test disabled
Add VM parameter - XX:-UseBiasedLocking to disable bias lock when the above test code runs
output
sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-00:34:34.398 [t1] c.Dog - synchronized in sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 08 f0 22 21 (00001000 11110000 00100010 00100001) (555937800) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-00:34:34.398 [t1] c.Dog - synchronized after sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
4) Test hashCode
- Normal objects do not have a hashCode at first, and are generated only after the first call
Latch - the hashCode of the calling object
The hashCode of the object is called, but the thread id is stored in Mark Word. If the hashCode is called, the biased lock will be revoked
- Lightweight locks record hashCode in the lock record
- The heavyweight lock will record the hashCode in the Monitor
Use bias lock after calling hashCode. Remember to remove - XX:-UseBiasedLocking
output
sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 1213186462 2022/03/06-00:38:07.855 [t1] c.Dog - synchronized in sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 68 f4 05 20 (01101000 11110100 00000101 00100000) (537261160) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-00:38:07.856 [t1] c.Dog - synchronized after sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 9e c1 4f (00000001 10011110 11000001 01001111) (1338088961) 4 4 (object header) 48 00 00 00 (01001000 00000000 00000000 00000000) (72) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Undo - objects used by other threads
When other threads use the biased lock object, the biased lock is upgraded to a lightweight lock
// Set bias lock non delayed start - XX:BiasedLockingStartupDelay=0 public static void main(String[] args) { Dog d = new Dog(); ClassLayout classLayout = ClassLayout.parseInstance(d); new Thread(() -> { synchronized (d) { log.debug(classLayout.toPrintable(d)); } synchronized (DogTest2.class) { DogTest2.class.notify(); } // If you do not use wait/notify to use join, you must open the following comments // Because: the t1 thread cannot end, otherwise the real estate thread may be reactivated by the jvm. The id of the low-level thread is the same as that of the t2 thread // try { // System.in.read(); // } catch (IOException e) { // e.printStackTrace(); // } }, "t1").start(); new Thread(() -> { synchronized (DogTest2.class) { try { DogTest2.class.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug(classLayout.toPrintable(d)); synchronized (d) { log.debug(classLayout.toPrintable(d)); } log.debug(classLayout.toPrintable(d)); }, "t2").start(); }
output
2022/03/06-00:46:57.794 [t1] c.DogTest2 - sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 40 48 1f (00000101 01000000 01001000 00011111) (524828677) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-00:46:57.794 [t2] c.DogTest2 - sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 40 48 1f (00000101 01000000 01001000 00011111) (524828677) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-00:46:57.794 [t2] c.DogTest2 - sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 38 f5 ee 20 (00111000 11110101 11101110 00100000) (552531256) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-00:46:57.794 [t2] c.DogTest2 - sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Undo - call wait/notify
Wait notify is only available for heavyweight locks, so it will also revoke the biased lock state
// Set bias lock non delayed start - XX:BiasedLockingStartupDelay=0 public static void main(String[] args) { Dog d = new Dog(); ClassLayout classLayout = ClassLayout.parseInstance(d); new Thread(() -> { log.debug(classLayout.toPrintable(d)); synchronized (d) { try { d.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } log.debug(classLayout.toPrintable(d)); }, "t1").start(); new Thread(() -> { try { TimeUnit.SECONDS.sleep(6); } catch (InterruptedException e) { e.printStackTrace(); } synchronized (d) { d.notify(); } log.debug(classLayout.toPrintable(d)); }, "t2").start(); }
output
2022/03/06-01:03:54.661 [t1] c.DogTest3 - sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 00 00 00 (00000101 00000000 00000000 00000000) (5) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:04:00.662 [t1] c.DogTest3 - sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 5a f5 51 1c (01011010 11110101 01010001 00011100) (475133274) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:04:00.662 [t2] c.DogTest3 - sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 5a f5 51 1c (01011010 11110101 01010001 00011100) (475133274) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Batch heavy bias
If the object is accessed by multiple threads, but there is no competition, the object biased to thread t1 still has the opportunity to re bias to t2, and the re bias will reset the Thread ID of the object
When the unbiased lock threshold is exceeded 20 times, the jvm will feel that it has done something wrong, so it will re bias to the locking thread when locking these objects
// Set bias lock non delayed start - XX:BiasedLockingStartupDelay=0 public static void main(String[] args) { List<Dog> list = new Vector<>(); new Thread(() -> { for (int i = 0; i < 30; i++) { Dog d = new Dog(); list.add(d); synchronized (d) { log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); } log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); } synchronized (list) { list.notify(); } }, "t1").start(); new Thread(() -> { synchronized (list) { try { list.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } for (int i = 0; i < 30; i++) { Dog d = list.get(i); log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); synchronized (d) { log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); } log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); } }, "t2").start(); }
Because the threshold value is 20, it's enough to pay extra attention to the situation that i=18, 19, 20 of t2 thread
output
2022/03/06-01:17:22.917 [t2] c.DogTest3 - 18 sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 c8 2e 1f (00000101 11001000 00101110 00011111) (523159557) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:17:22.917 [t2] c.DogTest3 - 18 sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) b0 ee a5 1f (10110000 11101110 10100101 00011111) (530968240) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:17:22.917 [t2] c.DogTest3 - 18 sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:17:22.917 [t2] c.DogTest3 - 19 sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 c8 2e 1f (00000101 11001000 00101110 00011111) (523159557) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:17:22.918 [t2] c.DogTest3 - 19 sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 d9 2e 1f (00000101 11011001 00101110 00011111) (523163909) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:17:22.918 [t2] c.DogTest3 - 19 sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 d9 2e 1f (00000101 11011001 00101110 00011111) (523163909) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:17:22.918 [t2] c.DogTest3 - 20 sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 c8 2e 1f (00000101 11001000 00101110 00011111) (523159557) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:17:22.918 [t2] c.DogTest3 - 20 sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 d9 2e 1f (00000101 11011001 00101110 00011111) (523163909) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total 2022/03/06-01:17:22.918 [t2] c.DogTest3 - 20 sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 05 d9 2e 1f (00000101 11011001 00101110 00011111) (523163909) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
Batch revocation
When the unbiased lock threshold is revoked more than 40 times, the jvm also feels that the bias is wrong, and maybe it should not be biased at all. Therefore, all objects of the whole class will become non biased, and the newly created objects will also be non biased
static Thread t1, t2, t3; // Set bias lock non delayed start - XX:BiasedLockingStartupDelay=0 public static void main(String[] args) throws InterruptedException { int loopNumber = 39; List<Dog> list = new Vector<>(); t1 = new Thread(() -> { for (int i = 0; i < loopNumber; i++) { Dog d = new Dog(); list.add(d); synchronized (d) { log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); } } LockSupport.unpark(t2); }, "t1"); t2 = new Thread(() -> { LockSupport.park(); for (int i = 0; i < loopNumber; i++) { Dog d = list.get(i); log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); synchronized (d) { log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); } log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); } LockSupport.unpark(t3); }, "t2"); t3 = new Thread(() -> { LockSupport.park(); for (int i = 0; i < loopNumber; i++) { Dog d = list.get(i); log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); synchronized (d) { log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); } log.debug(i + "\t" + ClassLayout.parseInstance(d).toPrintable(d)); } }, "t3"); t1.start(); t2.start(); t3.start(); t3.join(); Dog dog = new Dog(); log.debug(ClassLayout.parseInstance(dog).toPrintable(dog)); }
output
2022/03/06-01:30:38.705 [main] c.DogTest5 - sync.test.Dog object internals: OFFSET SIZE TYPE DESCRIPTION VALUE 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 8 4 (object header) a4 9b 01 f8 (10100100 10011011 00000001 11111000) (-134112348) 12 4 (loss due to the next object alignment) Instance size: 16 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes total
reference material
https://github.com/farmerjohngit/myblog/issues/12
https://www.cnblogs.com/LemonFive/p/11246086.html
https://www.cnblogs.com/LemonFive/p/11248248.html
Bias lock paper
5. Lock elimination
@Fork(1) @BenchmarkMode(Mode.AverageTime) @Warmup(iterations=3) @Measurement(iterations=5) @OutputTimeUnit(TimeUnit.NANOSECONDS) public class MyBenchmark { static int x = 0; @Benchmark public void a() throws Exception { x++; } @Benchmark public void b() throws Exception { Object o = new Object(); synchronized (o) { x++; } } }
java -jar benchmarks.jar
Benchmark Mode Samples Score Score error Units c.i.MyBenchmark.a avgt 5 1.542 0.056 ns/op c.i.MyBenchmark.b avgt 5 1.518 0.091 ns/op
java -XX:-EliminateLocks -jar benchmarks.jar
Benchmark Mode Samples Score Score error Units c.i.MyBenchmark.a avgt 5 1.507 0.108 ns/op c.i.MyBenchmark.b avgt 5 16.976 1.572 ns/op
Lock coarsening
Locking the same object multiple times leads to multiple reentry of the thread. You can use lock coarsening to optimize it, which is different from the granularity of subdivision lock mentioned earlier.