Read the JVM object creation and memory allocation mechanism

Posted by caramba on Tue, 14 Dec 2021 22:29:51 +0100

Dear friends, when we create a new object, how is the object produced? Let's talk about the object generation process and memory allocation mechanism. We can talk about it during the interview, which is definitely a bonus.

1. Check when loading class

During the execution of the virtual machine, Execute to new keyword (new keyword, object cloning, object serialization, etc.), the first step is to check whether the symbol reference corresponding to the parameter of the instruction is in the constant pool, and whether the corresponding class has been loaded, parsed and initialized. If it has, it means that the class has been loaded. If not, it means that the class has not been loaded, so the class record must be executed The whole process.

2. Memory allocation

After the class loading process is completed, the newly created object needs to be allocated memory. How to determine the specific size of the required memory? In fact, the size of the memory required by the object can be completely determined after the class loading. The virtual machine only needs to divide a fixed memory space of the corresponding size in the java heap.
However, there are two problems in allocating memory:
1. How to divide memory.
2. In the case of concurrency, it may occur that object A is allocating memory, the pointer has not been modified, and object B uses the original pointer to allocate memory at the same time.

There are two memory allocation methods for virtual machines, one is "pointer collision" and the other is "free list". java adopts pointer collision by default. For a regular java heap, the memory used is concentrated on one side of the heap, while the other side is free memory. When it is necessary to allocate a fixed size of memory, Just put the memory pointer (indicator of demarcation point) just move the corresponding size backward from the currently used position. When the heap memory allocation is not regular, the used memory and unused memory are staggered. It is difficult for the virtual machine to find a fixed size and continuous memory space. At this time, pointer collision is difficult to play a role. At this time, the virtual machine adopts the free list, The free list is used to maintain which memory blocks are free. When allocating memory, you only need to find a suitable and continuous memory block in the free list, and then update the record of this memory space on the free list.

Methods to solve concurrency problems:
CAS (compare and swap): the virtual machine uses CAS with failure retry to ensure the atomicity of update operation to synchronize the action of allocating memory space.

Thread local allocation buffer (TLAB): divide the memory allocation into different spaces according to threads, that is, each thread pre allocates a small piece of memory in the Java heap. Set whether the virtual machine uses TLAB through the - XX:+/-UseTLAB parameter (the JVM will open - XX: + usetlab by default), and - XX:TLABSize specifies the TLAB size.

3. Initialize zero value

After the memory allocation is completed, The virtual machine needs to initialize the allocated memory space to zero value (excluding the object header). If TLAB is used, this working process can also be carried out in advance when TLAB is allocated. This step ensures that the instance fields of the object can be directly used without initial values in Java code, and the program can access the zero value corresponding to the data type of these fields.

4. Set object header

After initializing the zero value, the virtual machine should make necessary settings for the object, such as which class instance the object is, how to find the metadata information of the class, the hash code of the object, the GC generation age of the object, etc. This information is stored in the Object Header of the object.

In the HotSpot virtual machine, The layout of objects stored in memory can be divided into three areas: object Header, Instance Data and Padding. The object Header of HotSpot virtual machine includes two parts of information. The first part is used to store the runtime data of the object itself, such as hash code (HashCode), GC generation age, lock status flag, lock held by thread, biased thread ID, biased timestamp, etc. another part of the object Header is the type pointer, that is, the pointer of the object to its class metadata. The virtual machine uses this pointer to determine which class instance the object is.

32-bit object header:

64 bit object header:

5. Implementation method

Execution method, that is, the object is initialized according to the programmer's wishes. At the language level, it is to assign a value to the attribute (note that this is different from the zero value assigned above, which is assigned by the programmer), and execute the construction method.

Object size and pointer compression
The object size can be viewed with JOL core package to introduce dependency

<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>
  import org.openjdk.jol.info.ClassLayout;

/**
 * Calculate object size
 */
public class JOLSample {

    public static void main(String[] args) {
        ClassLayout layout = ClassLayout.parseInstance(new Object());
        System.out.println(layout.toPrintable());

        System.out.println();
        ClassLayout layout1 = ClassLayout.parseInstance(new int[]{});
        System.out.println(layout1.toPrintable());

        System.out.println();
        ClassLayout layout2 = ClassLayout.parseInstance(new A());
        System.out.println(layout2.toPrintable());
    }

    // -20: + usecompressedoops compresses all pointers by default
    // -20: + usecompressedclasspointers the type pointer Klass Pointer in the compressed object header that is enabled by default
    // Oops : Ordinary Object Pointers
    public static class A {
                       //8B mark word
                       //4B Klass Pointer if compression is turned off - XX:-UseCompressedClassPointers or - XX:-UseCompressedOops, 8B is occupied
        int id;        //4B
        String name;   //4B if compression - XX:-UseCompressedOops is turned off, 8B is occupied
        byte b;        //1B 
        Object o;      //4B if compression - XX:-UseCompressedOops is turned off, 8B is occupied
    }
}


Operation results:
java.lang.Object object internals:
 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
      0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)    //mark word
      4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)    //mark word     
      8     4        (object header)                           e5 01 00 f8 (11100101 00000001 00000000 11111000) (-134217243)    //Klass Pointer
     12     4        (loss due to the next object alignment)
Instance size: 16 bytes
Space losses: 0 bytes internal + 4 bytes external = 4 bytes total


[I 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)                           6d 01 00 f8 (01101101 00000001 00000000 11111000) (-134217363)
     12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
     16     0    int [I.<elements>                             N/A
Instance size: 16 bytes
Space losses: 0 bytes internal + 0 bytes external = 0 bytes total


com.tuling.jvm.JOLSample$A 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)                           61 cc 00 f8 (01100001 11001100 00000000 11111000) (-134165407)
     12     4                int A.id                                      0
     16     1               byte A.b                                       0
     17     3                    (alignment/padding gap)                  
     20     4   java.lang.String A.name                                    null
     24     4   java.lang.Object A.o                                       null
     28     4                    (loss due to the next object alignment)
Instance size: 32 bytes
Space losses: 3 bytes internal + 4 bytes external = 7 bytes total

What is pointer compression for java objects?
1.jdk1. 6. Starting from update14, in the 64bit operating system, the JVM supports pointer compression
2.jvm configuration parameters: UseCompressedOops, compressed – compressed, OOP (ordinal object pointer) – object pointer
3. Enable pointer compression: - XX: + usecompressedoops (on by default), disable pointer compression: - XX:-UseCompressedOops

Why pointer compression?
1. Using 32-bit pointers (64 bits for actual storage) in HotSpot on 64 bit platform will increase the memory usage by about 1.5 times. Using a larger pointer to move data between main memory and cache will occupy a larger bandwidth, and GC will also be under great pressure

2. To reduce memory consumption on 64 bit platforms, enable pointer compression

3. In the jvm, the 32-bit address supports up to 4G memory (the 32nd power of 2), which can be optimized by compressing and encoding the object pointer when it is stored in the heap memory and decoding it after it is taken out to the cpu register (the object pointer is 32 bits in the heap and 35 bits in the register, and the 35th power of 2 = 32G), so that the jvm can support a larger memory configuration (less than or equal to 32G) with only 32-bit address

4. When the heap memory is less than 4G, it is not necessary to enable pointer compression. The jvm will directly remove the high 32-bit address, that is, use the low virtual address space

5. When the heap memory is greater than 32G, the compressed pointer will fail, and 64 bits (i.e. 8 bytes) will be forced to address java objects, which will lead to the problem of 1. Therefore, it is better not to exceed 32G

Object size calculation

In the 32-bit system, the space for storing Class pointer is 4 bytes, MarkWord is 4 bytes, and the object header is 8 bytes.

In a 64 bit system, the space for storing Class pointers is 8 bytes, MarkWord is 8 bytes, and the object header is 16 bytes.

When 64 bit pointer compression is enabled, the space for storing Class pointer is 4 bytes, markword is 8 bytes, and the object header is 12 bytes. Array length 4 bytes + array object header 8 bytes (object reference 4 bytes (64 bits without pointer compression are 8 bytes) + array markword 4 bytes (64 bits without pointer compression are 8 bytes)) + alignment 4 = 16 bytes.

Static properties are not included in the object size.

About aligned padding: for most processors, it is the most efficient way to access objects aligned and padded in 8-byte integer multiples.

Pay attention to the official account and receive the following information:
Reply 666 to get a full set of technical data;
Reply 888 to receive the large factory's face mail;
Reply 999, review the resume, push the big factory inside and screen directly.

Screenshot of the complete set of data:

Topics: Java jvm Interview