Java Object Header
from http://arturmkrtchyan.com/java-object-header
Have you ever wondered how java object looks like inside JVM ? Today’s post will mainly target the header part of the java object, will share how much memory is it consuming and what’s inside the header.
But before deep diving into the header part we should know that in HotSpot JVM every object has header and member fields. Additionally every object is 8 byte aligned in memory. To find out more on alignment check out memory structure post.
Every object (except array) in memory has 2 machine word header. The first one is calledmark word and the second one is klass word. Btw arrays have extra 32 bit word filled with array’s length.
Mark word stores identity hashcode, bits used for garbage collection, bits used for locking. To find out more check out the source from OpenJDK.
Klass word stores ordinary object pointer (oop) to class metadata, which describes the object’s layout, methods, and other type information. To find out more check out the metadata source from OpenJDK.
Now it’s the right time to deep dive and see what exactly is in header and specifically inmark word.
32 bit JVM
- identity_hashcode - identity hashcode of the object which is assigned lazily. If System.identityHashCode(obj) is called, it is calculated and written into the object header. When object is locked the identity hashcode value is moved into the monitor object.
- age - number of garbage collections the object has survived. It is incremented every time an object is copied within the young generation. When the age field reaches the value of max-tenuring-threshold, the object is promoted to the old generation.
- biased_lock - contains 1 if the biased locking is enabled for the class. 0 if the biased locking is disabled for the class. To find out more on biased locking check out my previous post.
- lock - the lock state of the object. 00 - Lightweight Locked, 01 - Unlocked or Biased, 10 - Heavyweight Locked, 11 - Marked for Garbage Collection. To find out more on locking/synchronization check out synchronization post.
- thread - when an object is biased towards specific thread instead of identity hash code mark word contains thread id. To find out more on biased locking check out my previous post.
- epoch which acts as a timestamp that indicates the validity of the bias.
- ptr_to_lock_record - When lock acquisitions are uncontended JVM uses atomic operations instead of OS mutexes. This technique is known as Lightweight Locking. In case of lightweight locking JVM sets a pointer to the lock record in the object’s header word via a CAS operation.
- ptr_to_heavyweight_monitor - If two different threads concurrently synchronize on the same object, the lightweight lock must be inflated to a Heavyweight Monitor for the management of waiting threads. In case of heavyweight locking JVM sets a pointer to the monitor in the object’s header word. To find out more on locking/synchronization check out synchronization post.
As we have seen every object on 32 bit jvm contains 8 byte header, 2 words each 4 byte. Arrays have an extra 32 bit word to store array size.
What about 64 bit JVM?
On 64 bit jvm every object contains 16 byte header, 2 words each 8 byte. Array has an extra 32 bit word to store array size.
Wait! What about Compressed oops ?
Let’s talk a bit about Ordinary Object Pointers (OOPs) and Compressed OOPs (Coops). OOPs are the handles/pointers the JVM uses as object references. When oops are only 32 bits long, they can reference only 4 GB of memory, which is why a 32-bit JVM is limited to a 4 GB heap size. (The same restriction applies at the operating system level, which is why any 32-bit process is limited to 4GB of address space.) When oops are 64 bits long, they can reference terabytes of memory.
What if there were 35-bit oops? Then the pointer could reference 32 GB of memory and still take up less space in the heap than 64-bit references. The problem is that there aren’t 35-bit registers in which to store such references. Instead, though, the JVM can assume that the last 3 bits of the reference are all 0. Now every reference can be stored in 32 bits in the heap. When the reference is stored into a 64-bit register, the JVM can shift it left by 3 bits (adding three zeros at the end). When the reference is saved from a register, the JVM can right-shift it by 3 bits, discarding the zeros at the end.
This allows JVM to use pointers that can reference 32 GB of memory while using only 32 bits in the heap. However it also means that the JVM cannot access any object at an address that isn’t divisible by 8, since any address from a compressed oop ends with three zeros. The first possible oop is 0x1, which when shifted becomes 0x8. The next oop is 0x2, which when shifted becomes 0x10 (16). Objects must therefore be located on an 8-byte boundary. As we know objects are already aligned on an 8-byte boundary in the JVM (in both the 32- and 64-bit versions); this is the optimal alignment for most processors. So nothing is lost by using compressed oops.
A program that uses a 31 GB heap and compressed oops will usually be faster than a program that uses a 33 GB heap. Although the 33 GB heap is larger, the extra space used by the pointers in that heap means that the larger heap will perform more frequent GC cycles and have worse performance.
Compressed oops are enabled using the -XX:+UseCompressedOops flag; in Java 7 and later versions, they are enabled by default whenever the maximum heap size is less than 32 GB. To find out more on CompressedOops check out this post.
64 bit Coops JVM
On 64 bit and CompressedOops enabled jvm every object contains 12 byte header, 8 byte mark word and 4 byte compressed klass oop. Array has an extra 32 bit word to store array size.
浙公网安备 33010602011771号