Information analysis of Java headers

Why should I look at Java object headers in the first place? Here is an excerpt from hotspot’s source code

The chart in adult readable form is shown below

Java object headers have different representations in different object states. There are three main states: unlocked state, locked state, and GC flag state. So I can understand that in Java, the lock can be understood to lock the object, that is, to change the state of the object head, if the lock is successful, then enter the synchronization code block. However, there are many kinds of locks in Java. As can be seen from the figure above, there are three lock states: biased lock, light lock and weight lock. The efficiency of these three locks is completely different. The analysis on efficiency will be analyzed in the following paragraphs. Only reasonable design code can we make reasonable use of locks. So we need to look at this object head first.

The layout of Java objects and the layout of object headers

1, JOL to analyze Java object layout

JOL ‐core</artifactId> 5 <dependency> 3 <groupId> org.openJDK. JOL </groupId> 4 <artifactId> JOL ‐core</artifactId> 5 The < version > 0.9 < / version > 6 < / dependency >Copy the code

A.java

1 public class A {2}Copy the code

JOLExample1.java

1 package com.luban.layout; 2 import org.openjdk.jol.info.ClassLayout; 3 import org.openjdk.jol.vm.VM; 4 import static java.lang.System.out; 5 6 public class JOLExample1 { 7 public static void main(String[] args) throws Exception { 8 out.println(VM.current().details()); 9 out.println(ClassLayout.parseClass(A.class).toPrintable()); 11 10}}Copy the code

The results

1 # Running 64‐bit HotSpot VM. 2 # Using ram with 0‐bit shift. 3 # Using RAM with 3‐bit shift  # Objects are 8 bytes aligned. 5 # Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] 6 # Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] 7 8 com.luban.layout.A object internals: 9 OFFSET SIZE TYPE DESCRIPTION VALUE 10 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 11 4 4  (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 12 8 4 (object header) 82 22 01 20 (10000010 00100010 00000001 00100000) (536945282) 13 12 4 (loss due to the next object alignment) 14 Instance size: 16 bytes 15 Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalCopy the code

Analysis Result 1

1 Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] [Oop(Ordinary Object Pointer), Boolean, byte, char, short, int, float, long, double] Size

The Object header is 12B, and 4B are aligned bytes (because objects must be multiples of 8 on 64-bit virtual machines). Since there are no fields in this Object, the instance data of this Object is 0B. Two questions

  • 1. What is instance data of an object?

  • 2. What does 12B store in the object header?

We can add A Boolean field to A, we know that Boolean field is 1B, and then we can see what the result is

A.java

1 public class A {2 Boolean flag =false; 4}Copy the code

Running Result 2

1 # Running 64‐bit HotSpot VM. 2 # Using ram with 0‐bit shift. 3 # Using RAM with 3‐bit shift  # Objects are 8 bytes aligned. 5 # Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] 6 # Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes] 7 8 com.luban.layout.A object internals: 9 OFFSET SIZE TYPE DESCRIPTION VALUE 10 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 11 4 4  (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 12 8 4 (object header) 82 22 01 20 (10000010 00100010 00000001 00100000) (536945282) 13 12 1 boolean A.flag false 14 13 3 (loss due to the next object alignment) 15 Instance size: 16 bytes 16 Space losses: 0 bytes internal + 3 bytes external = 3 bytes totalCopy the code

Analysis Result 2

The size of the entire Object remains the same. The size of the Object header is 12B, and the Boolean field flag is 1B. From this we can think of an Object layout as roughly divided into three parts: Object header and byte alignment of Object instance data

Next, why is the object header 12B? So what do we store in each of these 12B’s? (Different bits of VM object header length is different, here refers to the 64-bit VM) some technical terms for Java object headers

Openjdk.java.net/groups/hots… Start by referring to the explanation of object headers in the OpenJDK documentation

object header Common structure at the beginning of every GC-managed heap object. (Every oop points to an object header.) Includes fundamental information about the heap object’s layout, type, GC state, synchronization state, and identity hash code. Consists of two words. In arrays it is immediately followed by a length field. Note that both Java objects and VM- internal objects have a common object header format

A Java object header contains two Word objects and contains the heap object’s layout, type, GC state, synchronization state, and identity hash code. Which two Words?

mark word The first word of every object header. Usually a set of bitfields including synchronization state and identity hash code. May also be a pointer (with characteristic low bit encoding) to synchronization related information. During GC, may contain GC state bits.

The first word contains lock information, hashcode, GC information, etc. What is the second word?

klass pointer The second word of every object header. Points to another object (a metaobject) which describes the layout and behavior of the original object. For Java objects, the “klass” contains a C++ style “vtable”.

The second word in the object header, Klass Word, mainly points to the metadata of the object.

Assuming we understand that an object header consists mainly of the two parts shown above (except array objects, which also contain an array length), how big is a Java object header? We know from the JVM source comments that one Mark Word is 64bit, so what is the length of Klass?

So we need to find a way to get the details of the Java object header, verify its size, and verify that it contains the correct information.

According to the object header information printed by JOL above, it can be known that an object header is 12B, of which 8B is the Mark word, the remaining 4B is the Klass word, and the information related to the lock is the Mark word. Then, the information in the Mark Word will be analyzed

The first 56bit in markword stores the object’s Hashcode without locking

Java code and results:

1 public static void main(String[] args) throws Exception { 2 A a= new A(); 3 out.println("befor hash"); Before 4 / / not calculate HASHCODE object in the first five out of the println (ClassLayout. ParseInstance (a). ToPrintable ()); 6 / / JVM calculation of hashcode 7 out. Println (" JVM ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ "+ Integer. ToHexString (a.h ashCode ())); 8 HashUtil.countHash(a); Println ("after hash"); 10 out.println("after hash"); 11 out.println(ClassLayout.parseInstance(a).toPrintable()); 12 13 } 1 befor hash 2 com.luban.layout.A object internals: 3 OFFSET SIZE TYPE DESCRIPTION VALUE 4 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 5 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 6 8 4 (object header) 43 c1 00 20 (01000011 11000001 00000000 00100000) (5 36920387) 7 12 1 boolean A.flag false 8 13 3 (loss due to the next object alignment) 9 Instance size: 16 bytes 10 Space losses: 0 bytes Internal + 3 bytes External = 3 bytes Total 11 12 JVM ‐‐‐‐‐‐‐ 0x6AE40994 13 Util ‐‐‐‐‐‐ 0x6AE40994 14 after hash 15 com.luban.layout.A object internals: 16 OFFSET SIZE TYPE DESCRIPTION VALUE 17 0 4 (object header) 01 94 09 e4 (00000001 10010100 00001001 11100100) (‐469134335) 18 4 4 (Object header) 6A 000000 (01101010 00000000 00000000 00000000) (106) 19 8 4 (Object header) 43 C1  00 20 (01000011 11000001 00000000 00100000) (536920387) 20 12 1 boolean A.flag false 21 13 3 (loss due to the next object alignment) 22 Instance size: 16 bytes 23 Space losses: 0 bytes internal + 3 bytes external = 3 bytes total 24 25 26 Process finished with exit code 0Copy the code

Tool.oschina.net/hexconvert/ hexadecimal conversion

1 package com.luban.layout; 2 import sun.misc.Unsafe; 3 import java.lang.reflect.Field; 4 5 public class HashUtil { 6 public static void countHash(Object object) throws NoSuchFieldException, IllegalAccessException {7 / / manual calculation HashCode 8 Field Field = Unsafe. Class. GetDeclaredField (" theUnsafe "); 9 field.setAccessible(true); 10 Unsafe unsafe = (Unsafe) field.get(null); 11 long hashCode = 0; 12 for (long index = 7; index > 0; The index ‐ ‐) {/ / take Mark every Byte of Word to calculate 14 hashCode | = (unsafe. GetByte (object, index) & 0 XFF) < < ((index ‐ 1) * 8); 15 } 16 String code = Long.toHexString(hashCode); 17 System. Out. Println (" util ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ ‐ 0 x "+ code); 18 19} 20}Copy the code

Analysis Result 3:

1—– line 9 is the object header information before hashcode is printed. 56bits from 1-7b have no value. 16—- line 21 is the object header information after hashcode is printed. Because it’s small end storage. Among them, 12 lines are the result printed by hashcode method, and 13 lines are hashcode calculated by me based on 1-7B information, so it can be confirmed that the last seven bytes in mark Work in the Java object header store hashCode information. So the eight bits in the first byte store the age of the band, the information of biased locking, and the state of the object respectively. The information represented by the eight bits is shown in the figure below (in fact, there is information in the figure above). The figure will change with the state of the object

The object state is divided into five states, namely no lock, biased lock, light lock, weight lock and GC flag. Then, how can 2bit represent the five states (2bit can only represent the four states at most: (00,01,10,11), the JVM does a better job of representing the biased lock and the no-lock state as the same state, and then identifying the no-lock or no-lock state according to the biased lock identifier in the graph. What does that mean? Write a code analysis, before writing the code we first remember the information 00000001 in the no-lock state, and then write an example of biased locking to see the result

Java code and output:

1 package com.luban.layout; 2 import org.openjdk.jol.info.ClassLayout; 3 import static java.lang.System.out; 4 5 public class JOLExample2 { 6 static A a; 7 public static void main(String[] args) throws Exception { 8 //Thread.sleep(5000); 9 a = new A(); 10 out.println("befre lock"); 11 out.println(ClassLayout.parseInstance(a).toPrintable()); 12 sync(); 13 out.println("after lock"); 14 out.println(ClassLayout.parseInstance(a).toPrintable()); 15 } 16 17 public static void sync() throws InterruptedException { 18 synchronized (a){ 19 System.out.println(" I don't know what to print either "); 20 } 21 22 } 23 } 1 befre lock 2 com.luban.layout.A object internals: 3 OFFSET SIZE TYPE DESCRIPTION VALUE 4 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 5 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 6 8 4 (object header) 43 c1 00 20 (01000011 11000001 00000000 00100000) (5 36920387) 7 12 1 boolean A.flag false 8 13 3 (loss due to the next object alignment) 9 Instance size: 16 bytes 10 Space losses: 0 bytes internal + 3 bytes External = 3 bytes Total 11 12 I don't know what to print 13 After lock 14 com.luban.layout. an object internals: 15 OFFSET SIZE TYPE DESCRIPTION VALUE 16 0 4 (object header) 01 00 00 00 (00000001 00000000 00000000 00000000) (1) 17 4 4 (object header) 00 00 00 00 (00000000 00000000 00000000 00000000) (0) 18 8 4 (object header) 43 c1 00 20 (01000011 11000001 00000000 00100000) (536920387) 19 12 1 boolean A.flag false 20 13 3 (loss due to the next object alignment)Copy the code

Analysis Result 4

This program has only one thread to call sync, so it should be biased lock, but you will notice that the output (the first byte) is still 00000001, which is exactly the same as when the vm is not locked. In fact, this is because the vm starts with a delay for biased lock. For example, if you comment out sleep in the code above, the result will be different, Results will become 00000101, of course, in order to facilitate the test I can directly through the JVM parameter to disable delay – XX: + UseBiasedLocking – XX: BiasedLockingStartupDelay = 0, think about why biased locking will be delayed? (Except these 8bits, other bits store thread information and epoch, which is not captured here). It should be noted that after lock still retains bias information after exiting synchronization

Performance comparison favors locks and lightweight locks:

1 package com.luban.layout; 2 public class A { 3 int i; 4 public synchronized void parse(){ 5 i++; 6 } 7 } 1 package com.luban.layout; 2 import org.openjdk.jol.info.ClassLayout; 3 import static java.lang.System.out; 4 / / ‐ XX: BiasedLockingStartupDelay = 20000 ‐ XX: BiasedLockingStartupDelay = 0 5 public class JOLExample3 {6 public static void main(String[] args) throws Exception { 7 A a = new A(); 8 long start = System.currentTimeMillis(); 10 for(int I =0; int I =0; i<1000000000L; i++){ 12 a.parse(); 13 } 14 long end = System.currentTimeMillis(); 13 system.out.println (string.format ("% SMS ", end ‐ start)); 16 17} 18 19}Copy the code

So what is a lightweight lock? How does it work? Why slower than biased locking? Lightweight locks attempt to solve the thread synchronization problem at the application level without triggering the operating system’s mutex operation. Lightweight locks reduce the probability of multiple threads entering the mutex and cannot replace the mutex

First look at the object header of the lightweight lock

1 package com.luban.layout; 2 import org.openjdk.jol.info.ClassLayout; 3 import static java.lang.System.out; 4 5 public class JOLExample5 { 6 static A a; 7 public static void main(String[] args) throws Exception { 8 a = new A(); 9 out.println("befre lock"); 10 out.println(ClassLayout.parseInstance(a).toPrintable()); 11 sync(); 12 out.println("after lock"); 13 out.println(ClassLayout.parseInstance(a).toPrintable()); 14 } 15 16 public static void sync() throws InterruptedException { 17 synchronized (a){ 18 out.println("lock ing"); 19 out.println(ClassLayout.parseInstance(a).toPrintable()); 20} 21} 22}Copy the code

Performance comparison Light weight comparison weight:

1 package com.luban.layout; 2 3 import java.util.concurrent.CountDownLatch; 4 5 public class JOLExample4 { 6 static CountDownLatch countDownLatch = new CountDownLatch(1000000000); 7 public static void main(String[] args) throws Exception { 8 final A a = new A(); 9 10 long start = System.currentTimeMillis(); For (int I =0; int I =0; i<2; i++){ 15 new Thread(){ 16 @Override 17 public void run() { 18 while (countDownLatch.getCount() > 0) { 19 a.parse(); 20 } 21 } 22 }.start(); 23 } 24 countDownLatch.await(); 25 long end = System.currentTimeMillis(); 26 system.out.println (string.format ("% SMS ", end ‐ start)); 27 28} 29 30}Copy the code

First look at the object head for weight locking

1 package com.luban.layout; 2 import org.openjdk.jol.info.ClassLayout; 3 import static java.lang.System.out; 4 5 public class JOLExample6 { 6 static A a; 7 public static void main(String[] args) throws Exception { 8 //Thread.sleep(5000); 9 a = new A(); 10 out.println("befre lock"); 11 out.println(ClassLayout.parseInstance(a).toPrintable()); 12 13 Thread t1= new Thread(){ 14 public void run() { 15 synchronized (a){ 16 try { 17 Thread.sleep(5000); 18 System.out.println("t1 release"); 19 } catch (InterruptedException e) { 20 e.printStackTrace(); 21} 22} 23} 24}; 25 t1.start(); 26 Thread.sleep(1000); 27 out.println("t1 lock ing"); 28 out.println(ClassLayout.parseInstance(a).toPrintable()); 29 sync(); 30 out.println("after lock"); 31 out.println(ClassLayout.parseInstance(a).toPrintable()); 32 33 System.gc(); 34 out.println("after gc()"); 35 out.println(ClassLayout.parseInstance(a).toPrintable()); 36 } 37 38 public static void sync() throws InterruptedException { 39 synchronized (a){ 40 System.out.println("t1 main lock"); 41 out.println(ClassLayout.parseInstance(a).toPrintable()); 42} 43} 44}Copy the code

If the wait method is called, it immediately becomes a weight lock

1 package com.luban.layout; 2 3 import org.openjdk.jol.info.ClassLayout; 4 5 import static java.lang.System.out; 6 7 public class JOLExample7 { 8 static A a; 9 public static void main(String[] args) throws Exception { 10 //Thread.sleep(5000); 11 a = new A(); 12 out.println("befre lock"); 13 out.println(ClassLayout.parseInstance(a).toPrintable()); 14 15 Thread t1= new Thread(){ 16 public void run() { 17 synchronized (a){ 18 try { 19 synchronized (a) { 20 System.out.println("before wait"); 21 out.println(ClassLayout.parseInstance(a).toPrintable()); 22 a.wait(); 23 System.out.println(" after wait"); 24 out.println(ClassLayout.parseInstance(a).toPrintable()); 25 } 26 } catch (InterruptedException e) { 27 e.printStackTrace(); 28} 29} 30} 31}; 32 t1.start(); 33 Thread.sleep(5000); 34 synchronized (a) { 35 a.notifyAll(); 36} 37} 38}Copy the code

It is important to note that if the object has computed hashCode it cannot be biased

1 package com.luban.layout; 2 import org.openjdk.jol.info.ClassLayout; 3 import static java.lang.System.out; 4 5 public class JOLExample8 { 6 static A a; 7 public static void main(String[] args) throws Exception { 8 9 Thread.sleep(5000); 10 a = new A(); 11 a.hashCode(); 12 out.println("befre lock"); 13 out.println(ClassLayout.parseInstance(a).toPrintable()); 14 15 Thread t1= new Thread(){ 16 public void run() { 17 synchronized (a){ 18 try { 19 synchronized (a) { 20 System.out.println("lock ed"); 21 out.println(ClassLayout.parseInstance(a).toPrintable()); 22 } 23 } catch (Exception e) { 24 e.printStackTrace(); 25} 26} 27} 28}; 29 t1.start(); 30 31} 32}Copy the code

-end-

I heard that the fans of One button three even succeeded in the interview? If this article has helped you, please support it