“This is the third day of my participation in the First Challenge 2022, for more details: First Challenge 2022”.

Object creation

Java is an object-oriented programming language, and creating objects is usually just the new keyword.

Object creation process

When the virtual opportunity to a bytecodenewThe first check is to see if the argument to the instruction can locate a symbolic reference to a class in the constant pool. And check whether the class represented by the symbolic reference is loaded by the virtual machine class loader. If not, you have toPerform class loadingThe process.After the class has passed the check, the virtual machine will startNewly generated objects allocate memory. The amount of memory an object needs is determined at class load time. Object memory allocation is covered in a separate section below.

After memory allocation is complete, the virtual machine initializes the allocated memory space (excluding the object header) to zero, which is to clean up and initialize the memory space.

The virtual machine then needs to initialize the Object, such as the metadata (which class the Object is an instance of), the hash code of the Object, the GC generation age of the Object, and the state of the bias lock, all of which are stored in the Object Header.

By completing the above process, we have actually completed the creation of memory in the virtual machine, but we have only just started from the perspective of new object creation in Java. We still need to call the constructor to initialize the object (and possibly call the constructor of the parent class, initialization block, and so on before and after).

Initialize a Java object. That is, the

() method is called from the.class perspective. If other methods are called in the constructor, they are executed, and the creation of the Java object is not complete until all associated methods in the constructor have been executed.

Object memory allocation

The object memory allocation process is shown in the following figure:The task of allocating space for an object is essentially to assign a certain size chunk of memory from the Jvm’s memory region to a Java object. (The default is to allocate on the heap).

Pointer to the collision

The Java heap is assumed to be absolutely clean, with all used memory on one side and unused memory on the other. A pointer is placed in the middle to indicate their cut-off point. The allocated memory merely moves The Pointer an equal distance to The size of The Java object into The free direction. This allocation is called “dumping The Pointer”

The free list

However, if the Memory in the Java heap is not tidy, and the used memory blocks are interlocked with the free memory blocks, there is no simple way to do pointer collisions, and the virtual machine must maintain a list of available memory areas. Note which memory blocks are available. At the time of object memory allocation, a large enough memory space is found from the list to allocate to the instance object, and the records in the list are updated. This type of allocation is called a Free List.

Select the memory allocation mode

When to use pointer collisions and when to use free lists? The choice of allocation is determined by the cleanliness of the Java heap, which in turn is determined by the ability of the adopted garbage collector to Compact.

  • When Serial, ParNew and other collectors with pointer compression and collation process are used, the allocation algorithm adopted by the system is pointer collision, which is simple and efficient.
  • When using a CMS collector based on the Sweep algorithm, memory can theoretically only be allocated using a complex free list.

Concurrent memory allocation scheme

In the process of frequent object allocation, even if only one pointer is changed, it is not thread-safe in the case of concurrency. It may occur that object A is allocating memory, and object B uses the original pointer for internal allocation before the pointer is modified. There are two possible solutions to this problem:

  • One is to synchronize the memory allocation action – in effect, the virtual machine uses CAS + retry to ensure atomicity of the update operation.

  • Each thread allocates a small chunk of memory in the Java heap in advance. This is called the Thred Local Allocation Buffer (TLAB). The thread allocates the memory. Memory is allocated on that thread, and is allocated in that thread’s local buffer. Lock synchronization is required only when the local buffer is used up and a new buffer is allocated.

Object memory layout

In the HotSpot VIRTUAL machine, the layout of objects stored in memory can be divided into three areas:Object Header, Instance Data, and alignment Padding. Here is the data structure of a normal object instance versus an array object instance:

Object header structure

Mark Word (64bit)

With openJDK source markOop. HPP we can see

Two pointer variables are described:

Ptr_to_lock_record: pointer to the lock record in the stack in the lightweight lock state. When lock acquisition is uncontested, the JVM uses atomic operations instead of OS mutexes, a technique known as lightweight locking. In the case of lightweight locking, the JVM sets a pointer to the lock record in the object’s Mark Word via a CAS operation.

Ptr_to_heavyweight_monitor: pointer to the object Monitor Monitor in the heavyweight lock state. If two different threads are competing on the same object at the same time, lightweight locking must be upgraded to Monitor to manage waiting threads. In the case of heavyweight locking, the JVM sets a pointer to Monitor on the object’s ptr_TO_HEAVYweight_monitor.

In markOop. HPP we can see the following comments for the file

// part omitted // 32 bits: // -------- // hash:25 ------------>| age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:23 epoch:2 age:4 biased_lock:1 lock:2 (biased object) // size:32 ------------------------------------------>| (CMS free block) // PromotedObject*:29 ---------->| promo_bits:3 ----->| (CMS promoted object) // // 64 bits: // -------- // unused:25 hash:31 -->| unused:1 age:4 biased_lock:1 lock:2 (normal object) // JavaThread*:54 epoch:2 unused:1 age:4 biased_lock:1 lock:2 (biased object) // PromotedObject*:61 --------------------->| promo_bits:3 ----->| (CMS promoted object) // size:64 ----------------------------------------------------->| (CMS free block) // // unused:25 hash:31 -->| cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && normal object) // JavaThread*:54 epoch:2 cms_free:1 age:4 biased_lock:1 lock:2 (COOPs && biased object) // narrowOop:32 unused:24 cms_free:1 unused:4 promo_bits:3 ----->| (COOPs && CMS promoted object) // unused:21 size:35 -->| cms_free:1 unused:7 ------------------>| (COOPs && CMS free Block) // partially omittedCopy the code

klass

Klass corresponds to the Java CLass. An object JVM generates a kClass instance object that stores metadata information into the Java CLass object. In JDK 1.8, this is stored in the metadata space. The object stores a pointer of type Klass, a pointer to the object’s class metadata, which the virtual machine uses to determine which class the object is an instance of.

Array length (only array objects have it)

If the object is an array, there must also be a piece of data in the object header to record the length of the array.

The instance data

The instance data part is the valid information that the object actually stores, as well as the content of various types of fields and methods defined in the program code. Both inherited from a parent class and defined in a subclass are documented here.

Alignment filling

The third alignment padding does not necessarily exist and has no special meaning. It serves only as a placeholder. Since HotSpot VM’s automatic memory management system requires that the object’s starting address be an integer multiple of 8 bytes, in other words, the object’s size must be an integer multiple of 8 bytes. The object header is exactly a multiple (1 or 2) of 8 bytes, so when the object instance data part is not aligned, it needs to be filled by alignment.

Object size calculation

  1. On 32-bit systems, the size of the Class pointer is 4 bytes, the MarkWord is 4 bytes, and the object header is 8 bytes.
  2. On 64-bit systems, the size of the Class pointer is 8 bytes, the MarkWord is 8 bytes, and the object header is 16 bytes.
  3. 64-bit with pointer compression enabled, the size of the Class pointer is 4 bytes, the MarkWord is 8 bytes, and the object header is 12 bytes. Array length 4 bytes + array object header 8 bytes (object reference 4 bytes (8 bytes for 64-bit without pointer compression)+ array Markword 4 bytes (8 bytes for 64-bit without pointer compression)+ alignment 4=16 bytes.
  4. Static properties are not included in the object size.

Print object state

JOL (Java Object Layout) is an open source widget for analyzing the Layout of objects in the JVM. Unsafe, JVMTI, and Serviceability Agent (SA) were used to decode the actual object layout, footprint, and references. This makes JOL more accurate than other tools that rely on heap dumps, specification assumptions, and so on. Maven repository dependencies are as follows:

<dependency>
  <groupId>org.openjdk.jol</groupId>
  <artifactId>jol-core</artifactId>
  <version>0.16</version>
</dependency>
Copy the code

1. View the internal information of the object, including the field layout, title information, field values, and alignment/fill.ClassLayout.parseInstance(obj).toPrintable() 2, View the external information of the object:GraphLayout.parseInstance(obj).toPrintable() 3, view objects take up memory space size: GraphLayout. ParseInstance (obj). TotalSize ()

16
Copy the code

Complete code:

import org.openjdk.jol.info.ClassLayout;
import org.openjdk.jol.info.GraphLayout;

public class ObjectTest2 {

    public static void main(String[] args) {
        Object obj = newObject(); System.out.println(ClassLayout.parseInstance(obj).toPrintable()); System.out.println(); System.out.println(); System.out.println(GraphLayout.parseInstance(obj).toPrintable()); System.out.println(); System.out.println(); System.out.println(GraphLayout.parseInstance(obj).totalSize()); }}Copy the code

Object access location

Handle access

With handle access, a block of memory may be allocated to the Java heap as a handle pool. Reference stores the handle address of the object, and the handle contains the specific address information of the instance data and type data of the object. Its structure is shown in the figure below:

Direct access to the

For direct pointer access, the memory layout of the object in the Java heap must consider how to place the relevant information of the access type data. The direct storage in reference is the address of the object. If only the object itself is accessed, there is no need for the overhead of another indirect access, as shown in the following figure:

Object access mode comparison

The two methods of object access have their own advantages. The biggest advantage of using handles for access is that reference stores a stable handle address and only changes the instance data pointer in the handle when the object is moved (which is a very common behavior in garbage collection). Reference itself does not need to be modified. The biggest benefit of using direct Pointers is that they are faster. It saves time for a pointer location. Because object accesses are very frequent in Java, these overhead can add up to a very significant execution cost. It mainly uses the second method for object access (with exceptions, including an extra forward if the Shenandoah collector is used), but it is also common to use handles for access in various languages and frameworks across the software development spectrum.

The resources

  • In-depth Understanding of THE JVM Virtual Machine third edition by Zhiming Zhou
  • www.cnblogs.com/jhxxb/p/109…
  • www.cnblogs.com/maxigang/p/…
  • www.oracle.com/technetwork…
  • github.com/openjdk/jol