Bytecode analysis creation process

Simple implementation of a code to create an object

public class ObjectTest { public static void main(String[] args) { VM vm = new VM(); }}Copy the code

The process of creating an object from a bytecode analysis

0 new #2 <com/alaske/test/VM>
3 dup
4 invokespecial #3 <com/alaske/test/VM.<init> : ()V>
7 astore_1
8 return
Copy the code

new

The new instruction allocates a certain amount of memory to hold the data of an object. The size of an object can be determined. For example, an int takes up 4 bytes and a reference takes up 4 bytes

dup

Dup is equivalent to pushing the reference of the corresponding memory address onto the stack. Then, the corresponding stack will have two object references, one for object assignment and the other for object method invocation

invokespecial

The invokespecial directive is the constructor that calls the object and initializes the object. I’m using the default constructor. Empty parameter constructs are used to initialize object data, and when new the JVM assigns default values to global variables of the object

astore

The ASTore instruction stores a reference to the corresponding object in a local variable table

The JVM analyzes the creation process

The Class is loaded

When you create an object, you check whether the object and its parent interface are loaded or not and you can look at the Class loading process that I wrote earlier, If it is not loaded, the parent delegate mechanism is used to load the corresponding Class and other classes associated with the Class into the method area. For data structures in the method area, see the JVM runtime data area

Memory allocation

From a bytecode perspective, we know that the new instruction allocates memory space, but how does the JVM allocate memory space?

There are two ways to allocate, one is memory space and if it’s contiguous like the copy and mark-up algorithm that you use then it’s contiguous, If memory is contiguous, it is allocated in a way called pointer collisions. If memory is not contiguous, for example, the JVM maintains a FreeList of free memory for a FreeList. Several garbage collection algorithms have been introduced before

For example, multiple threads are creating different objects at the same time. The storage space of the heap is also shared by threads. There will also be thread safety problems when allocating space. The first method is to use TLAB to allocate 1% space for each thread in Eden area as the preferred area of memory allocation. If TLAB is not used, CAS retry will be used to ensure thread safety. I have posted the source code description of this TLAB before

Pointer to the collision

When we say that memory is contiguous, we allocate memory using pointer collisions

The pointer is moved to the end of the last memory address requested, and on the next memory request, the pointer moves the object size to the free memory area

The free list

When the memory is not continuous, for example, we use the tag removal algorithm as a way of memory management, so it’s not contiguous space memory space can produce memory fragments Like I said before CMS is tag removal methods used by the garbage collector, this time affirmation won’t be able to adopt the way of pointer collision to open space, So it maintains a list store of free space and looks for the right size of memory when allocating it

Default initialization

public class VM  {

    private int value = 100;
    private Object data;

}
Copy the code

For example, we define value and data in our object

The JVM can also use objects that we don’t have to do with assignment in order to implement it. In the application space, the object is assigned null, the base data type is assigned the default value, and the reference data is assigned null

For example, the code above will assign value to 0 and data to null, which is the default value, not assignment

Initialize the init

Following the default initialization code view, we can look at the bytecode when executing the

constructor function of the object

 0 aload_0
 1 invokespecial #1 <java/lang/Object.<init> : ()V>
 4 aload_0
 5 bipush 100
 7 putfield #2 <com/alaske/test/VM.value : I>
 10 return
Copy the code

Aload_0 takes the index 0 from the local variable table, and the index 0 in non-static methods is our this current object,bipush and putfield, one that pushes 100 into our operand stack,putfield vm. value

Set the object header

The Object header contains three parts, here involves the Object’s memory Layout JOL(Java Object Layout), I use JOL here a reference to print the Object header, I use Gradle

The compile "org. Its. Jol: jol - core: 0.9"Copy the code
com.alaske.test.VM 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) 44 c0 00 f8 (01000100 11000000 00000000 11111000) (-134168508) 12 4 int VM.value 100 16 4 java.lang.Object VM.data null 20 4 (loss due to the next object alignment) Instance size: 24 bytes Space losses: 0 bytes internal + 4 bytes external = 4 bytes totalCopy the code

The data stored in object headers and what they do will be described in a separate article

Object access mode

Handle to the pool

A pool of handles to objects is divided into an area in the heap area. The handle pool holds reference object entity data and object type data (in the method area)

Disadvantages:

Obviously, we need to create a separate space to record the handle pool storage, and the access needs to be a little less efficient

Advantages:

When the object data changes like the garbage collector needs to defragment the object and move it around and copy it, the references in the Stack space don’t need to be changed you just need to change the references in the handle pool

Direct reference

Hotspot uses a direct reference approach by accessing the type data in the method area via the Klass Pointer in the object header written above

Disadvantages:

The Stack pointer needs to be modified when the object data changes the reference

Advantages:

There is no need to create an extra space to store references, adding Pointers to the original object header can be used to access object entity data efficiently