preface
The previous method we analyzed the Java virtual machine implementation process and method overloading and rewriting theory, and analyzes the method invocation of the process and principle of the Java virtual machine thread stack is private, there is no data security issues, and compared with the Java virtual machine stack the heap in terms of more complex, because the heap is all threads share a piece of memory space, Thread-safety issues arise, and garbage collection is mainly about recollecting space in the heap, so it’s important to understand the layout of the heap in depth. Now let’s move on to the in-heap layout and the in-memory layout of Java objects.
Object pointing
Let’s start with some code:
package com.zwx.jvm; public class HeapMemory { private Object obj1 = new Object(); public static void main(String[] args) { Object obj2 = new Object(); }}Copy the code
In the above code, what is the difference between obj1 and obj2 in memory?
Let’s start by rememberingThe JVM series 1The method area stores the structure of each class, such as the runtime constant pool, property and method data, and method and constructor data. So obj1 is the method area, and new creates an object instance, which is stored in the heap, resulting in the following image (The method area points to the heap) :
Obj2, on the other hand, is a local variable that belongs to a method and is stored in a local variable table in a stack frame in the Java virtual machine stack, which is classicThe stack to heap:
If a variable is referred to the heap and only an instance object is stored in the heap, how does the example object in the heap know which Class it belongs to, i.e. how does the instance know its corresponding Class element? This relates to how a Java object is laid out in memory.
Java memory model
The object memory can be divided into three areas: Header, Instance Data, and Padding.Using a 64-bit operating system as an example (without pointer compression enabled)The Java object layout looks like this:
Where the object header in Mark Word is detailed in the articleMechanism of synchronized lock upgradeIn detail.
The alignment padding shown in the figure above is not mandatory, and if the object header and instance data add up to a multiple of 8 bytes, then alignment padding is not required.
With the Java memory layout in mind, let’s look at an interview question
Object obj= New Object() Occupied bytes
The size of the new Object() can be divided into two types:
- The size of Pointer compression that is disabled is as follows: 8(Mark Word)+8(Class Pointer)=16 bytes
- Pointer compression enabled (default enabled) When Pointer compression is enabled, Class Pointer is compressed to 4 bytes with a final size of 8(Mark Word)+4(Class Pointer)+4(align fill)=16 bytes
Is this the result? So let’s verify that. First we introduce a POM dependency:
<dependency>
<groupId>org.openjdk.jol</groupId>
<artifactId>jol-core</artifactId>
<version>0.10</version>
</dependency>
Copy the code
Then create a simple demo:
package com.zwx.jvm; import org.openjdk.jol.info.ClassLayout; public class HeapMemory { public static void main(String[] args) { Object obj = new Object(); System.out.println(ClassLayout.parseInstance(obj).toPrintable()); }}Copy the code
The following output is displayed:
The final result is 16 bytes, no problem, this is because pointer compression is enabled by default, so we will now turn pointer compression off and try again.
-xx :+UseCompressedOops Disables pointer compression. -xx: -usecompressedoops disables pointer compressionCopy the code
Run it again and get the following results:
As you can see, there is no alignment padding left, but the size is still 16 bits.
Now let’s demonstrate the size of an object with attributes.
Create a new class with a single byte property inside:
package com.zwx.jvm;
public class MyItem {
byte i = 0;
}
Copy the code
Then output the size of the class with pointer compression on and pointer compression off.
package com.zwx.jvm; import org.openjdk.jol.info.ClassLayout; public class HeapMemory { public static void main(String[] args) { MyItem myItem = new MyItem(); System.out.println(ClassLayout.parseInstance(myItem).toPrintable()); }}Copy the code
Enable pointer compression, occupying 16 bytes:
Disable pointer compression, occupying 24 bytes:
At this point, you can see the advantage of having pointer compression enabled. If you are constantly creating a large number of objects, pointer compression can be optimized for performance.
Object access
After creating an object, of course, we need to access it, so when we need to access an object, how to locate the object? At present, there are two main ways to access objects: handle access and direct pointer access.
- Handle access
With handle access, the Java virtual machine allocates a chunk of memory in the heap to store the handle pool, which stores the handle address in the object, and then stores the object instance data and object type data address in the handle pool.
- Direct pointer access (Hot Spot VIRTUAL machine)
Direct pointer access will store the object type data directly in the object.
Handle access vs. direct pointer access
Contrast the above graph we can easily, is if you use the handle to access, plus a pointer position, but he also has an advantage is that, if an object is moving (address changed), then only need to change handle pool is ok, don’t need to modify the point to inside the reference objects, and if you use a direct pointer to access, It is also necessary to modify the reference direction in the local variation table.
Heap memory
We mentioned above that the Mark Word in the Java object header stores the generational age of the object.
The generational age of an object can be interpreted as the number of garbage collections. If an object still exists after one garbage collection, the generational age increases by 1. In a 64-bit VM, the generational age accounts for four bits and the maximum is 15. The generation age defaults to 0000 and increases with the number of garbage collections.
Java heap memory is divided into Young area and Old area according to generation age. Objects are allocated to Young area first and enter Old area when they reach a certain generation age (-xx :MaxTenuringThreshold can be set to 15 by default) (note: If an object is too large, it goes directly to the Old section.
The reason for this division is that if the entire heap has only one extents, then garbage collection will need to scan all objects in the heap each time, wasting performance. But it was her most Java object life cycle is short, once an object recycling recycling for so many times not to drop, can think of the next garbage collection time can also not to drop, so Young and the Old area separately from the garbage can, only when Young area after the garbage collected or no room, Then trigger the Old section garbage collection.
Young area
Let’s look at the following scenario, where Young is an overview after garbage collection:
If say now to an object, takes up 2 the size of the object, are found not bottom go to, this time will trigger GC (GC), but once triggered the GC (GC), thread is influential to the customer, because in the process of the GC to make sure that the object reference not changing, need to stop all user threads, Sun call this event: Stop the World (STW). This will be covered in more detail in the next article on garbage collection, but I won’t go into it here.
So is generally less GC, the better, and actually in the figure above you can see at least can also be put into three objects, as long as according to the object are put away in the correct order, then can be put down, so this creates a problem, the space is there, but because the space is discontinuous, cause the failure of application memory object, lead to trigger GC, so how to solve this problem?
The solution is to put the objects in the Young area in order, so a method is generated to divide the Young area again, divided into two areas:Eden areaandSurvivor area.
The specific operation is as follows: When an object comes, it is allocated to Eden area first, and when Eden area is full, GC is triggered. After GC, in order to prevent space discontinuity, the surviving objects are copied to Survivor area, and then Eden area can be completely cleaned up. Of course, there is a premise to do this.That is, most objects have a very short life cycle, so most objects in Eden can be reclaimed in one garbage collection(This premise is summed up by testing).
When GC is triggered, Survivor zone will also be reclaimed together, which does not mean that only Eden zone is triggered, but then the problem arises again. The Eden zone ensures that the space is basically continuous, but Survivor zone may generate space debris, which leads to discontinuity, so Survivor zone is divided into two:
At this point the workflow looks like this again:
First Eden area allocation, Eden area after full trigger GC, GC after surviving object is copied to the S0 area (S1 area is empty), and then continue to object in Eden area distribution, fire again if it is found that S0 area after GC will not fit the (generation of space debris, the space and actually), then the S0 area objects are copied to the S1 area, And the surviving object is also copied to S1, S0 is empty at this time, and repeated operations, if the S0 area or S1 area space object copy moved still can not put, it means that this time is really full, then go to the old area to borrow some space (this isGuarantee mechanismFull GC is raised if there is not enough space in the old region. OutOfMemeoyError is raised if there is not enough space in the old region.
Note: To ensure that each copy between S0 and S1 is successful, the size of S0 and S1 must be the same, and one region must be empty at a time. While this may result in a small amount of wasted space, it’s worth it in terms of other performance improvements.
Old district
When an object in the Young section reaches the set generation age, the object will enter the Old section. When the Old section is Full, the Full GC will be triggered. If the Old section cannot clear the space, OutOfMemeoyError will be raised.
Noun literacy
There are a lot of new terms mentioned above, and there are actually other names for many of them, so I think it’s worth knowing.
- Garbage collection: referred to as GC.
- Minor GC: GC for the new generation
- Major GC: For old GC, the old GC triggers Minor GC as well as Full GC.
- Full GC: Simultaneous Cenozoic + old GC.
- Young: The new generation
- The Old days
- Eden: I haven’t found any Chinese translation yet.
- Surcivor zone: Survival zone
- S0 and S1: also called from and to. Note that from and to are constantly changing identities, and S0 and S1 must be equal, and an area must be empty
A diagram of an object’s life trajectory
From the above introduction, you should have a general impression that an object will be constantly transferred in Eden, S0, S1 and Old (except short-lived objects that will be recovered at the beginning of course). We can get the following flow chart:
conclusion
This article mainly introduces how a Java object is stored in the heap, and demonstrates the size of a common object in combination with the memory layout of Java objects, and then analyzes the space division in the heap and the reason for the division. In this article, GC related knowledge is not explained in depth. GC and GC algorithms and GC collectors will be discussed in detail in the next article.