012Java virtual machine 003 data storage

Some of the content comes from the following Blogs:

https://www.cnblogs.com/xrq730/p/4827590.html

https://www.cnblogs.com/gw811/p/2730117.html

https://www.cnblogs.com/jhxxb/p/11001238.html

1 memory area model

The Java virtual machine defines the memory area that the program needs to use when running. The model is as follows:

The reason why so many areas are divided is that these areas have their own purposes and the time of creation and destruction.

Some areas exist with the start of the virtual machine process, while others are destroyed and established depending on the start and end of the user thread.

The green part in the figure is the memory area shared by all threads, including method area and heap. The white part is the unique data area of thread runtime, including virtual machine stack, local method stack and program counter.

2 runtime data area

2.1 PROGRAM COUNTER REGISTER

2.1.1 function

It is used to store the address pointing to the next instruction, that is, the instruction code to be executed, so that the execution engine can read the next instruction.

If the thread is executing a Java method, this counter records the address of the virtual machine bytecode instruction being executed. If the Natvie method is being executed, the counter value is empty (Undefined).

2.1.2 thread private

In order to recover to the correct execution position after thread switching, each thread needs to have an independent program counter. The counters between threads do not affect each other and are stored independently.

2.1.3 abnormality

This memory area is the only area where no OutOfMemoryError condition is specified in the virtual machine specification.

2.2 JAVA STACK, virtual machine stack

2.2.1 function

Virtual machine stack describes the memory model of Java method execution. When each method is executed, a stack frame will be created at the same time to store local variable table, operand stack, dynamic link, method return address and other information.

Each method is called until the execution is completed, which corresponds to the process of a stack frame from entering the stack to exiting the stack in the virtual machine stack. This process follows the rule of first in and last out.

In the active thread, only the stack frame at the top of the stack is valid, which is called the current stack frame, and the method associated with this stack frame is called the current method.

2.2.2 thread private

Like the program counter, the virtual machine stack is also thread private. Its life cycle is the same as that of the thread. Each time a thread is created, a virtual machine stack will be created accordingly. Therefore, the virtual machine stack is also a thread private memory area.

2.2.3 abnormality

In the Java virtual machine specification, two exceptions are specified for this area:

1) If the stack depth requested by the thread is greater than the depth allowed by the virtual machine, that is, too many recursive calls, a StackOverflowError exception will be thrown:

1 public static void test() {
2      test();
3 }
4 public static void main(String[] args) throws Exception {
5      test();
6 }

2) The local array is too large. When the array defined inside the function is too large, it may lead to memory overflow and throw StackOverflowError exception.

3) If the virtual machine stack can be dynamically expanded (most virtual machines can be dynamically expanded at present, but fixed length virtual machine stacks are also allowed in the virtual machine specification), an OutOfMemoryError exception will be thrown when enough memory cannot be applied for during expansion.

2.2.4 stack frame

How much memory needs to be allocated for a stack frame will not be affected by the variable data during program running, but only depends on the specific virtual machine implementation.

1) Local Variable Table

Local variable table is a set of variable value storage space, which is used to store method parameters and local variables defined inside the method, including various basic types, reference types and return address types (pointing to the address of a bytecode instruction).

If it is a basic type variable, its variable name and value are stored in the method stack. If it is a reference type variable, its variable value actually stores the memory address value, and the object pointed to is stored in the heap.

The memory space required by the local variable table is allocated during compilation. The size of the local variable table will not be changed during method operation. The specific size can be seen in the compiled class file.

The capacity of the local variable table takes the Variable Slot as the minimum unit, and each Variable Slot can store 32-bit memory space.

long and double data of 64 bit length will occupy two variable slots, and other data types only occupy one variable slot.

2) Operand Stack

The operand stack can also be sized at compile time.

When the stack frame is created, the operation stack is empty. In the process of method execution, various bytecode instructions will write and extract contents into the operand stack, that is, out of stack and in stack operations. For example, when doing arithmetic operations, it is through the operand stack, or when calling other methods, it is through the operand stack to pass parameters.

3) Dynamic Linking

Each stack frame contains a reference to the method to which the stack frame belongs in the runtime constant pool. This reference is held to support dynamic links during method calls.

In the parsing phase of the class loading phase, symbolic references are converted to direct references, which is also called static parsing. The other part will be converted to direct reference at run time, which is called dynamic link.

4) Return Address

After the method starts executing, there are only two ways to exit: method return instruction and exception exit.

Regardless of any exit method, after the method exits, it needs to return to the location where the method is called before the program can continue to execute. When the method returns, it may need to save some information in the stack frame.

2.3 NATIVE METHOD STACK

2.3.1 function

The role of the local method stack is very similar to that of the virtual machine stack. The difference is that the virtual machine stack executes Java method services for the virtual machine, while the local method stack serves the Native methods used by the virtual machine.

2.3.2 thread private

Like the virtual machine stack, the local method stack is thread private.

2.3.3 abnormality

Like the virtual machine stack, the local method stack area will throw StackOverflowError and OutOfMemoryError exceptions.

2.3.4 local methods

When Java was born, C/C + + was rampant. In order to gain a foothold, it must call C/C + + programs. Therefore, the native keyword is used to identify the method that calls C/C + + programs. This method is called native method.

In order to distinguish from the virtual machine stack used to process Java methods, a special area is opened in memory for processing Native methods, which is called the local method stack.

At present, this method is used less and less, except for hardware related applications, such as driving printers through Java programs or managing production devices through Java systems. It is rare in enterprise applications, because the communication between heterogeneous fields is very developed, such as Socket communication, web service and so on.

For example, the hashCode() method in the Object class is a local method:

1 public native int hashCode();

2.4 HEAP

2.4.1 function

The only purpose of this memory area is to store object instances. Almost all object instances allocate memory here.

2.4.2 thread sharing

Heap is a memory area shared by all threads. It is created when the virtual machine starts. The stored data is not thread safe.

2.4.3 abnormality

According to the Java virtual machine specification, the heap can be in physically discontinuous memory space as long as it is logically continuous, just like our disk space. When implemented, it can be either fixed or scalable, but the current mainstream virtual machines are implemented according to scalability.

If there is no memory in the retirement area to complete the instance allocation and the heap cannot be expanded, an OutOfMemoryError exception will be thrown.

The common causes of abnormality are:

1) Insufficient heap memory settings.

2) A large number of objects are created in the code and cannot be collected by the garbage collector for a long time.

2.4.4 Division

Heap is the main area managed by garbage collector, so it is often called GC heap.

From the perspective of memory recycling, the heap can be subdivided into the new generation and the old generation because the current collectors basically adopt the generational collection algorithm.

1) Cenozoic

The newly generated objects are preferentially stored in the Cenozoic. The Cenozoic objects live and die day and night, and the survival rate is very low.

In the new generation, a garbage collection with conventional application can generally recover 70% to 95% of the space, and the recovery efficiency is very high.

The Cenozoic can be subdivided into Eden space, From Survivor space and To Survivor space, and the default ratio is 8:1:1. This division is to facilitate garbage collection (GC).

2) Old age

Large objects will be directly allocated to the elderly generation, and long-term surviving objects will also be allocated to the old age.

The life cycle of objects in the elderly generation is long, the survival rate is relatively high, the frequency of GC in the elderly generation is relatively low, and the recovery speed is relatively slow.

When the memory in the old age is full, a GC will be triggered. If the memory space after GC is still not satisfied, an OOM exception will be triggered.

2.5 METHOD AREA

2.5.1 function

The method area is used to store class information, constants, static variables, code compiled by the real-time compiler and other data that have been loaded by the virtual machine.

Although the virtual machine specification describes the method area as a logical part of the heap, it has an alias called non heap, which should be distinguished from the heap.

2.5.2 thread sharing

The method area, like the heap, is a memory area shared by each thread.

2.5.3 abnormality

According to the virtual machine specification, when the method area cannot meet the memory allocation requirements, an OutOfMemoryError exception will be thrown.

The common causes of abnormality are:

1) Insufficient memory settings.

2) Load a large number of third-party Jar packages.

3) There is a lot of code that calls reflection.

2.5.4 permanent replacement

For developers who are used to developing and deploying programs on the HotSpot virtual machine, many people are willing to call the method area Permanent Generation. In essence, the two are not equivalent, just because the design team of the HotSpot virtual machine chooses to extend the GC generation collection to the method area, or use the Permanent Generation to implement the method area. For other virtual machines (such as BEA JRockit, IBM J9, etc.) there is no concept of Permanent Generation.

Because unreasonable parameter setting of permanent generation will cause problems, too small parameter is easy to generate OOM, and too large parameter will lead to space waste, so meta space is used to replace permanent generation in JDK1.8.

In addition, removing the permanent generation is an effort to integrate HotSpot and JRockit. JRockit does not have a permanent generation and does not need to configure a permanent generation.

2.5.5 constant pool

The Runtime Constant Pool is used to store various literal quantities (constants and strings) and symbolic references (full class name, property name, method name and modifier) generated during compilation.

JDK1.6 and before have a permanent generation, and the constant pool is in the method area. JDK1.7 has a permanent generation, but it has been gradually removed from the permanent generation, and the constant pool is in the heap. JDK1.8 and after have no permanent generation, and the constant pool is in the meta space.

Putting the constant pool in the heap can reclaim memory in time and avoid memory waste.

2.5.6 waste recycling

The virtual machine specification has very loose restrictions on this area. In addition to not requiring continuous memory like heap and choosing fixed size or scalability, you can also choose not to implement garbage collection.

Garbage collection tasks in this area:

1) Recycling of discarded constants.

2) Uninstall of type.

3 object memory structure

3.1 memory structure diagram

The memory structure of the object is as follows:

The internal structure of an object is divided into object header, instance data, and alignment fill.

3.2 object header

The object header is divided into object tag and type pointer. If it is an array object, it also has array length.

3.2.1 object marking

The data stored in the object tag will be reused according to the lock state of the object. During operation, the data in the object tag will change with the change of the lock flag bit.

In a 64 bit HotSpot virtual machine, the object tag occupies 8 bytes.

3.2.2 type pointer

Object points to its class metadata. The virtual machine uses this pointer to determine which class instance this object is.

When compression is enabled, it takes 4 bytes, otherwise it takes 8 bytes. JDK1.8 turns on compression by default.

3.2.3 array length

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.

Because the virtual machine can determine the size of the object through the metadata information of the ordinary object, but the size of the array cannot be determined from the metadata of the array.

The array length takes 4 bytes.

3.3 instance data

It is used to store the valid information of the object, including various types of fields defined in the program code (including those inherited from the parent class and self declared). The rules are as follows:

1) Fields of the same width are always assigned together.

2) Variables defined in the parent class appear before the child class.

3) If the CompactFields parameter of the virtual machine is true (the default is true), the narrow variable of the subclass may be inserted into the gap of the parent variable.

3.4 align fill

The virtual machine requires that the starting address of the object must be an integer multiple of 8 bytes. Padding data does not have to exist, just for byte alignment.

4 create object

4.1 mode

Use the new keyword: the most common way.

Use the newInstance() method of Class object: through reflection, you can only call the constructor of empty parameter, which must be public permission.

Use the newInstance() method of the Constructor object: any defined Constructor can be called through reflection without permission requirements.

Use the clone() method: no constructor is called. The current class needs to implement the clonable interface to implement the clone() method.

Use deserialization: get the binary stream of objects from files and networks, and then deserialize them into objects.

Use third-party libraries: for example, created through Objenesis.

4.2 steps

4.2.1 loading

Check whether the symbolic reference of a class can be located in the constant pool, and check whether the class represented by the symbolic reference has been loaded, linked and initialized. If not, the class initialization process must be performed first.

4.2.2 allocating memory

After the class loading check passes, the virtual machine allocates memory for the new object. The memory size required by the object can be completely determined after the class is loaded. Allocating space for the object is nothing more than dividing a certain size of memory from the heap.

There are two ways to allocate memory:

1) Pointer collision method

If the memory is regular, the virtual machine will use the pointer collision method to allocate memory for the object.

All used memory is on one side and free memory is on the other side. A pointer is placed in the middle as an indicator of the dividing point. Allocating memory is just moving the pointer to the free side for a distance equal to the size of the object.

If the garbage collector selects the tag collation algorithm based on SerialOld and ParallelOld, the virtual machine adopts this allocation method.

2) Free list method

If the memory is not regular and the used memory and unused memory are staggered, the virtual machine will use the free list method to allocate memory for the object.

The virtual machine maintains a list to record which memory blocks are available. When reallocating, find a large enough space in the list to divide it into object instances, and update the contents of the list.

If the garbage collector selects the CMS based mark clear algorithm, the virtual machine adopts this allocation method.

4.2.3 handling concurrent security issues

The next problem to be solved is how to ensure thread safety when creating objects.

Because the virtual machine is allocating memory to object A, the pointer has not been modified, and object B uses the original pointer to allocate memory at the same time.

CAS (Compare and Swap, that is, compare and replace, which is a common technology in implementing concurrent algorithms) failure retry and TLAB (Thread Local Allocation Buffer, which divides the private allocation buffer of multiple threads) area locking are used to solve the problem of concurrency security.

4.2.4 allocated space

After the memory allocation, the virtual machine initializes the allocated memory space to zero (excluding the object header).

This step ensures that the instance fields of the object can be used directly without assigning initial values in the code, and the program can access the zero value corresponding to the data type of these fields.

4.2.5 setting object header

The class of the object, the hash value of the object, the GC information and lock information of the object are stored in the object header of the object.

How this process is set up depends on the JVM implementation.

4.2.6 initialization

Initialize the member variable, execute the instantiated code block, call the construction method of the class, and assign the first address of the object in the heap to the reference variable.

5 access object

5.1 description

If there is the following code in the method body:

Object obj = new Object();

"Object obj" represents a reference type data in the local variable table of the stack.

"new Object()" means a piece of structured memory in the heap that stores all Instance Data values of Object type (Instance Data, data of each instance field in the Object).

In addition, the heap must also contain address information that can find this object type data (such as object type, parent class, implemented interface, method, etc.), and these type data are stored in the method area.

5.2 access mode

Because the reference type only specifies a reference to an object in the virtual machine specification, it does not define how the reference should be located and the specific location of the object in the heap. Therefore, the object access methods implemented by different virtual machines will be different.

There are two mainstream access methods:

1) Using the handle access method, a piece of memory will be divided in the heap as the handle pool. The reference stores the handle address of the object, and the handle contains the specific address information of the object instance data and type data.

2) For direct pointer access (the method adopted by HotSpot), the layout of heap objects must consider how to place the relevant information of access type data. The object address is directly stored in reference.

Differences between the two object access methods:

The biggest advantage of using the handle access method is that the reference stores a stable handle address. When the object is moved (moving the object during garbage collection is a very common behavior), it will only change the instance data pointer in the handle, and the reference itself does not need to be modified.

The biggest advantage of using direct pointer access is that it is faster. It saves the time cost of pointer positioning. Because object access is very frequent in Java, this kind of cost adds up to a considerable execution cost.

As far as the main virtual machine HotSpot is concerned, it uses the second method for object access, but from the perspective of the whole scope of software development, it is also very common for various languages and frameworks to use handles for access.

6 escape analysis

6.1 definitions

Escape Analysis is simply a technology that the virtual machine can analyze the usage range of newly created objects and decide whether to allocate memory on the heap.

An object created in the method may be returned to the external method after the method is finished. It may also be introduced as a parameter when other methods are invoked in the method. The above two situations are called object escape.

According to the scope, it can be divided into the following three situations:

GlobalEscape: a reference to an object has escaped from a method or thread. For example, the reference of an object is assigned to a class variable or static variable, and the object follower method returns to the variable of another method, or is stored in an object that has escaped.

ArgEscape (parameter level escape): when invoking other methods in the method, the reference of the object is passed to other methods as a parameter.

NoEscape (no escape): the scope of the object is only in this method. It is generated as the method stack frame enters the stack and dies as it exits the stack. In this case, the object can be allocated on the stack, but not necessarily on the stack.

6.2 use

JVM parameters for escape analysis are as follows:

1 Enable escape analysis:-XX:+DoEscapeAnalysis
2 Turn off escape analysis:-XX:-DoEscapeAnalysis
3 Display analysis results:-XX:+PrintEscapeAnalysis

Escape analysis technology is supported after JDK1.7, and is set to enabled by default. You don't need to add this parameter.

6.3 optimization

Use escape analysis to judge an object. If there is no escape, the compiler can optimize the code.

6.3.1 synchronous elimination

If an object can only be accessed by one thread, the object can be used as a lock, or the synchronization operation of the object can be ignored after virtual machine optimization, which is the lock elimination technology in multithreading.

JVM parameters for synchronous elimination are as follows:

1 Open lock elimination:-XX:+EliminateLocks
2 Close lock elimination:-XX:-EliminateLocks

Synchronization cancellation is enabled by default in JDK1.8, and should be based on escape analysis.

6.3.2 scalar substitution

Scalar refers to data that cannot be decomposed into smaller data. The original data type in Java is scalar.

In contrast, data that can also be decomposed is called Aggregate. An object in Java is an Aggregate because it can be decomposed into other aggregates and scalars.

In the compilation stage, if it is found that an object will not be accessed by the outside world through escape analysis, after JIT compiler optimization, the object will be disassembled into several member variables to replace it. This process is scalar substitution.

The JVM parameters for scalar substitution are as follows:

1 Enable scalar substitution:-XX:+EliminateAllocations
2 Turn off scalar substitution:-XX:-EliminateAllocations
3 Display scalar substitution details:-XX:+PrintEliminateAllocations

Scalar substitution is also enabled by default in JDK1.8, and should be based on escape analysis.

6.3.3 on stack allocation

If an object does not escape, it may be optimized and stored in the stack, but it is not absolutely stored in the stack, or it may still be stored in the heap.

It should be noted that in the existing virtual machines, there is no real allocation on the stack, which is actually realized through scalar replacement.

When the object does not escape, the object can be decomposed into member scalars through scalar replacement and allocated in the stack memory, which is consistent with the life cycle of the method. With the destruction of the stack frame when it comes out of the stack, the GC pressure is reduced and the application performance is improved.

7 code demonstration

7.1 Guide Package

Introduce dependency in pom.xml file:

1 <!-- JOL rely on -->
2 <dependency>
3     <groupId>org.openjdk.jol</groupId>
4     <artifactId>jol-core</artifactId>
5     <version>0.9</version>
6 </dependency>

When reading object information, the virtual machine reads from high to low in groups of 8 bits.

7.2 turn off compression

In a 64 bit HotSpot virtual machine, the type pointer needs to occupy 8 bytes.

Starting from JDK 1.6, 64 bit virtual machines support the use compressed OOP option. It can compress OOP (Ordinary Object Pointer) to occupy only 4 bytes, so as to save memory.

Under JDK1.8, this option is enabled by default.

You can explicitly configure by adding virtual machine parameters:

1 -XX:+UseCompressedOops // Turn on pointer compression
2 -XX:-UseCompressedOops // Turn off pointer compression

In IDEA, you can modify VM options and add parameters through the Edit Configurations menu in the Run menu:

7.3 execute code and analyze

7.3.1 viewing Object

The code is as follows:

1 public static void main(String[] args){
2     System.out.println(ClassLayout.parseInstance(new Object()).toPrintable());
3 }

The results are as follows:

1 java.lang.Object object internals:
2 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
3       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
5       8     4        (object header)                           00 1c 53 1c (00000000 00011100 01010011 00011100) (475208704)
6      12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
7 Instance size: 16 bytes
8 Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

Parameter Description:

OFFSET: OFFSET, indicating the number of bytes from which to start.

SIZE: occupied byte SIZE.

TYPE: TYPE defined in Class.

DESCRIPTION: DESCRIPTION of the type.

VALUE: the VALUE in memory.

The analysis is as follows:

An empty Object takes 16 bytes, an Object tag takes 8 bytes, and a type pointer takes 8 bytes after compression is turned off.

7.3.2 viewing Integer objects

The code is as follows:

1 public static void main(String[] args){
2     System.out.println(ClassLayout.parseInstance(new Integer(1)).toPrintable());
3 }

The results are as follows:

 1 java.lang.Integer object internals:
 2 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
 3       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
 4       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
 5       8     4        (object header)                           e0 71 5e 1c (11100000 01110001 01011110 00011100) (475951584)
 6      12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
 7      16     4    int Integer.value                             1
 8      20     4        (loss due to the next object alignment)
 9 Instance size: 24 bytes
10 Space losses: 0 bytes internal + 4 bytes external = 4 bytes total

The analysis is as follows:

Compared with empty Object objects, the total size takes 24 bytes. Among them, the Object tag is still 8 bytes and the content is the same, and the pointer type is still 8 bytes, but the content changes. The instance data occupying 4 bytes and the alignment filling occupying 4 bytes are added.

Because the length of int type is 32 bits, that is, 4 bytes, the size of instance data is 4 bytes. In order to ensure that the total size is a multiple of 8, an additional 4 bytes of alignment filling is added.

7.3.3 viewing Long objects

The code is as follows:

1 public static void main(String[] args){
2     System.out.println(ClassLayout.parseInstance(new Long(1)).toPrintable());
3 }

The results are as follows:

1 java.lang.Long object internals:
2 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
3       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
4       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
5       8     4        (object header)                           f0 ac ba 1c (11110000 10101100 10111010 00011100) (481996016)
6      12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
7      16     8   long Long.value                                1
8 Instance size: 24 bytes
9 Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

The analysis is as follows:

Compared with the Integer object, the total size is still 24 bytes. Among them, the object tag and pointer type change little, but the size occupied by the instance data changes to 8 bytes, and there is no alignment filling.

Because the length of the long type is 64 bits, that is, 8 bytes, the instance data takes up 8 bytes and does not need to be aligned and filled.

7.3.4 viewing array objects

The code is as follows:

1 public static void main(String[] args) {
2     System.out.println(ClassLayout.parseInstance(new Integer[]{1, 2, 3}).toPrintable());
3 }

The results are as follows:

 1 [Ljava.lang.Integer; object internals:
 2 OFFSET  SIZE                TYPE DESCRIPTION                               VALUE
 3       0     4                     (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
 4       4     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
 5       8     4                     (object header)                           00 42 e6 1c (00000000 01000010 11100110 00011100) (484852224)
 6      12     4                     (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
 7      16     4                     (object header)                           03 00 00 00 (00000011 00000000 00000000 00000000) (3)
 8      20     4                     (alignment/padding gap)                  
 9      24    24   java.lang.Integer Integer;.<elements>                       N/A
10 Instance size: 48 bytes
11 Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

The analysis is as follows:

The array object will increase the array length in the object header, occupying 4 bytes, and a value of 3 indicates that the length is 3. In addition, 4 bytes of alignment filling is required in the object tag.

The instance data occupies 24 bytes.

7.3.5 viewing generation information

The code is as follows:

1 public static void main(String[] args){
2     Object o = new Object();
3     System.out.println(ClassLayout.parseInstance(o).toPrintable());
4     System.gc();
5     System.out.println(ClassLayout.parseInstance(o).toPrintable());
6 }

The results are as follows:

 1 java.lang.Object object internals:
 2 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
 3       0     4        (object header)                           01 00 00 00 (00000001 00000000 00000000 00000000) (1)
 4       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
 5       8     4        (object header)                           00 1c a2 1c (00000000 00011100 10100010 00011100) (480386048)
 6      12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
 7 Instance size: 16 bytes
 8 Space losses: 0 bytes internal + 0 bytes external = 0 bytes total
 9 
10 java.lang.Object object internals:
11 OFFSET  SIZE   TYPE DESCRIPTION                               VALUE
12       0     4        (object header)                           09 00 00 00 (00001001 00000000 00000000 00000000) (9)
13       4     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
14       8     4        (object header)                           00 1c a2 1c (00000000 00011100 10100010 00011100) (480386048)
15      12     4        (object header)                           00 00 00 00 (00000000 00000000 00000000 00000000) (0)
16 Instance size: 16 bytes
17 Space losses: 0 bytes internal + 0 bytes external = 0 bytes total

The analysis is as follows:

When reading the object header, the virtual machine reads every 8 bits as a group from high to low, so among the 8 bytes representing the object mark, the first printed 8-bit number is actually the last 8-bit number.

When the information stored in the control object header is not garbage collected, the top 8 bits are as follows: the first bit is invalid, the last 4 bits represent generational age, the last 1 bit represents biased lock, and the last 2 bits represent lock flag.

Before garbage collection, the generation age is 0. After garbage collection, the generation age becomes 1. The generation age of 4 digits means that the maximum age is 15.

 

Tags: Java

Posted on Fri, 03 Dec 2021 07:43:20 -0500 by jagdpenta