Java virtual machine automatic memory management mechanism-theory

Mr. Qian Zhongshu said: Java's automatic memory management mechanism is a besieged city. People outside want to come in, but people inside the wall want to go out...
(Otherwise, I won't learn JVM here. qwq)

1. A preliminary exploration of Java memory

(1) Division of memory area

When JVM executes, it divides the memory area it manages into different areas. These memory areas have their own purposes and life cycles.
Note: The memory model given below is only given by the Java virtual machine specification 概念模型, the specific implementation will be completed by the specific virtual machine.
Insert picture description here

1. Program counter

The program counter is also called the PC register. It is the only memory area defined in the Java virtual machine specification that does not throw OOM exceptions . This area occupies a small amount of memory. Change the value of the counter to get the next byte to be executed. Code instructions .

2. Java virtual machine stack

The virtual machine stack is a thread memory model that describes the execution of java methods. The execution of each java method corresponds to 栈帧the process of creating, pushing and popping each of the virtual machine stacks . The stack frame is used to store 局部变量表and 操作数栈other information, the most important of which should be the local variable table information.
The local variable table stores various basic data types and object references that can be known at compile time . The basic storage unit is 局部变量槽.

3. Local method stack

The function of the local method stack is similar to that of the virtual machine stack, except that the local method stack serves the virtual machine to execute non-java methods . In the Java virtual machine specification, there are no mandatory provisions on the specific details of the local method stack, and even the local method stack and the virtual machine stack are combined in the mainstream virtual machine HotSpot.

4.Java heap

As the largest part of the Java memory, the Java heap exists for the sole purpose of storing object instances . At the same time, the Java heap is also a memory area managed by the GC garbage collector.
In order to facilitate the allocation and recovery of heap memory, the shared heap space can also be divided into allocation buffers private to multiple threads.
According to the "Java Virtual Machine Specification", the Java heap allows discontinuity in memory, but it must be logically continuous . In different virtual machine implementations, the Java heap can be a fixed size or expandable, which is determined by the virtual machine itself.

5. Method area

5.1

The method area is mainly used to store type-related information, static variables, constants, code caches, etc. that have been loaded by the virtual machine .

It is worth mentioning that in previous versions of jdk8, the method area of ​​the mainstream virtual machine HotSpot was designed with permanent generation. Starting with jdk8, the metaspace design was adopted.

注意:在JDK8之后静态变量存放在堆当中,此时的静态变量在方法区就只是存在于逻辑上的概念了

Metaspace and permanent generation .

5.2 Runtime constant pool

A piece of code, various literals and symbol references generated after the compilation period are stored in the constant pool table in the class file, and after the class loading process, this part of the data will be stored in the runtime constant pool. The biggest difference between the runtime constant pool and the class file constant pool is its dynamic nature, which allows data to be dynamically placed in the pool during execution.

Supplement: direct memory

Direct memory is not part of the JVM memory area. Its size is limited by the total memory of the machine and the addressing ability of the CPU. It is suitable for occasions that require large memory and frequent access.
The JVM stores the direct memory address reference object in the Java heap: DirectByteBuffer, which is used to complete the access to the off-heap memory.

(2) The processing of the object in memory

Let's take mainstream virtual machines HotSpotand the most commonly used areas Java堆as examples to understand the process of creating, laying out, and accessing Java objects in the Java heap .

1. Object creation

When the virtual machine encounters the new instruction, it will first proceed 类加载检查(to ensure that the class loading process must be executed first), then the Java object 分配内存, at the same time 将字段信息初始化为零值, then proceed 对象头信息的初始化(HashCode, metadata information, etc.), and finally 执行<init>方法, complete the creation of the object.

There are two main memory allocation strategies for objects: 指针碰撞and 空闲列表, which correspond to the centralized distribution and discrete distribution of memory respectively.

Since the creation of objects is very frequent, we need to ensure absolute thread safety. Mainly provide the following two solutions:

a. Synchronize the action of allocating memory space. The HotSpot virtual machine uses CAS lock + failure retry to ensure the atomicity of the update operation.
b. Divide the memory allocation action into different spaces, pre-allocate a local thread allocation buffer (TLAB) for each thread, and perform synchronization lock after the TLAB is used up. Whether the strategy is used can -XX:+/-UseTLABbe determined by parameters

2. Layout of object data (composition)

In addition to the 实例数据structure of Java object data in memory , it also includes 对齐填充(acting as a placeholder: in the HotSpot virtual machine, the starting address of the object must be an integer multiple of 8) and 对象头数据.

The object header data stores 运行时数据(Mark Word)and 类型指针:

The runtime data stores a lot of data information that has nothing to do with the data defined by the object itself. In order to save space, a dynamically defined data structure is adopted (that is, different states are represented by setting flag bits, and different contents are stored in different states. ).

The virtual machine determines the corresponding class of the object through the type pointer, but not all objects need to retain the type pointer (the reason is: when accessing the object through the handle, it stores both the object instance address and the address of the type data). In addition, if the object is an array, the length information of the array needs to be retained.

3. Object access

Java programs reference引用locate Java objects on the stack. There are two mainstream access methods:
Insert picture description here
Insert picture description here
each has its own advantages:

When using handle access, when the object address changes, only the value of the handle needs to be changed, not the value of the reference.
When using direct pointer access, the biggest advantage is the increase in speed.

Two. Garbage collector

One of the biggest differences between Java, C and C++ languages ​​is its automatic garbage collection mechanism. But when faced with memory overflow and memory leaks in some specific environments, we need to troubleshoot and optimize performance ourselves. We already know the division of the Java memory area. The local method stack, virtual machine stack, and program counter are private parts of the thread. They live and die together with the thread. This part of the area does not need GC, the place where GC mechanism is needed is the Java heap and method area.

The first thing to confirm in thought is that, like people, there has never been a garbage collector that is perfect everywhere . Only by considering specific application scenarios and choosing the best garbage collector and parameter combination can the best performance be obtained. (The purpose of learning the Java Virtual Machine is also here)

(1) Object survival judgment algorithm (heap collection)

The most straightforward decision algorithm is the reference counting algorithm : the counter is +1 when there is a reference, -1 when the reference is invalid, and the object is determined to be dead until the counter is 0. The algorithm is too simple and does not consider some special cases. For example, when an object is cyclically referenced, even if the object can never be accessed again, the counter is still not 0, and the garbage collector is there looking at it and does not collect it.
Insert picture description here
The current mainstream judgment algorithm is 可达性分析算法: take some GC Rootsroot objects called " " as the starting node set, search downwards according to the reference relationship, and the path traversed is called " 引用链". When an object no longer has a reference chain to GC Roots, it indicates that the object is dead . (However, this is in probation period, and the finalize() method of the object will be tried before the object is actually recovered. This is the object's last chance to redeem itself! (This method is not recommended for various reasons))
Can be used as a GC Roots are: inside the virtual machine references, constants references, static references, permanent exception object, object references and so on .

Supplement: Garbage collection in method area

The method area is not required to implement garbage collection in the "Java Virtual Machine Specification", because compared to the heap collection, the cost-effectiveness of the method area recovery is too low (limited by harsh judgment conditions). However, in some specific situations, it is necessary to implement garbage collection in the method area: reflection, dynamic proxy and other places where custom class loaders are frequently generated.
Then, the method area stores constants and class-related information, and naturally recycled objects are also constants and class-related information.

(2) Garbage collection algorithm (idea)

1. Preface: Generational Collection Theory

Three basic hypotheses:
1. The vast majority of objects are dying day and night (always die in the darkness before dawn)
2. Objects that have been garbage collected many times but have not been collected will become more difficult to recycle (dimensional Jing Yongzhe Beifeng made)
3. Cross-generation references are only a small number (two objects with the same frequency and the same rhythm generally exist in the same place)

Obviously, if the objects are stored in partitions according to the age , each partition adopts different garbage collection mechanisms according to its different characteristics, which will greatly improve the efficiency.
In addition, according to the third hypothesis, for the problem of cross-generation references, we do not need to scan the entire memory during the garbage collection process, only need to create one 记忆集, record the memory unit that has cross-generation references , and scan this small part during GC The recording unit is sufficient.

2. Common collection algorithm ideas

2.1 Mark-sweep algorithm

Mark surviving or dead objects, and then clear unmarked or marked objects.

The main problem is that it will 产生大量的内存碎片.

"In-depth understanding of the Java virtual machine" said that there is another problem: when most of the objects in the heap need to be recycled, a lot of marking and cleaning must be done, and the efficiency will be reduced at this time.
Personally, I don't quite understand what the author means. Since the reclaimed objects in the heap account for a relatively large amount, we can in turn only mark the objects that are not reclaimed. Why must a lot of marking work be done? I think what the author wants to express is only the problem of low efficiency when the number of objects to be removed increases. (Because the work of removing a large number of objects is inevitable)

2.2 Mark-copy algorithm

The memory is divided into regions, and only a part of the memory space is used each time. When the region is full, the surviving objects are copied to another region, and the original memory region is released in batches at the end. Suitable for 新生代this kind of memory area with few surviving objects.

The problem of memory fragmentation is solved, but it has occurred 内存浪费. At the same time 需要有其他区域提供内存担保(because in actual application scenarios, the area division ratio is far greater than 1:1, but no one can guarantee that the remaining memory area can accommodate the current All surviving objects)

2.3 Marking-sorting algorithm

Move all surviving objects into a boundary, and then remove the objects outside the boundary.

Although this algorithm is also an extremely burdensome operation when facing areas with high survival rates such as the old age, compared to mark-copy, it is 不再需要额外的内存担保more suitable for garbage in areas with high survival rates such as old age objects. collect. A conceivable solution is to use the mark-and-sweep algorithm most of the time, and execute the mark-and-sweep algorithm only when the memory fragments are too much to bear.

(3) Garbage First collector

1. Epoch-making problem-solving ideas

The biggest difference between the G1 collector and the previous collectors is that it uses 化整为零the idea of yes . The G1 collector retains the concept of generation, but the generation area is not fixed. G1 determines the smallest unit of memory recovery as Regioneach region can play the role of Eden, Survivor and the old space, and uses different recovery strategies to deal with garbage at the same time.

The G1 collector is well implemented 停顿时间模型: the best recovery effect is achieved within the pause time given by the user. The user specifies the pause time through the -XX:MaxGCPauseMills parameter, and the G1 collector then performs garbage collection according to the priority list (sorting the regions according to the maximum recovery revenue).

It is worth mentioning that the G1 collector does not pursue absolute cleanliness after every garbage collection, as long as the current memory is sufficient for users to use, this is enough. (Very in line with people’s Buddhist thinking haha)

But this needs to be noted that the pause time cannot be set too small, because in the end it may be that the speed of memory cleaning cannot keep up with the speed of memory allocation.

2. Operation process and detailed realization

2.1 Solutions to the problem of object cross-generation reference

As I said before, we don't need to scan the entire heap area for the cross-generation problem of object references. We only need to record the memory units that have cross-generation reference objects through the memory set and scan the memory units recorded in this part.

The memory set here is an abstract data structure, which is implemented in the HotSpot virtual machine in 卡表the form of a memory set, maintaining a certain size of byte array as the value value (the default size in HotSpot is 512 bytes). The memory is regarded as a smaller area. If there are cross-generation references of objects in this area, they are identified, the corresponding key value is said to be dirty, and finally the value area corresponding to the dirty value is added to GC Roots Scan . On this basis, the G1 collector adopts the form of a two-way card meter, not only recording "who I point to", but also "who points to me".

In HotSpot in use 写屏障to achieve the maintenance of the card table elements. The simple understanding of the write barrier is the additional logic code for operations such as marking before and after the reference assignment statement of the object . In addition, in order to avoid 伪共享the problem (data processing in the same cache line will affect efficiency), you can add a judgment statement before executing the dirty mark: if there is no mark, then mark (details, true details, each line of code All are well thought out). Whether the judgment is enabled can be determined by the -XX:+UseCondCardMark parameter .

2.2 Separation of User Thread and Collection Thread in Concurrent Process

The G1 collector is performed concurrently in the scanning and marking phase, that is, it will not "Stop the World". The realization of this effect needs to solve two problems:

1. Ensure that the structure of the original object graph will not be changed during the execution of the user thread.
2. In the concurrent collection process, new objects are constantly being created while the collection thread is performing garbage collection, so memory allocation for new objects is also a problem to be considered.

For the first question, first understand the three-color mark:
Insert picture description here
简单理解为 white represents the dead object; black represents the certain surviving object, the object can only survive when it is referenced by the black object, and the black object will not return to rescan ! ! ! Gray is between white and black (equivalent to the buffer in the scanning process).

Then the problem is coming. When the user thread and the collection thread are concurrently running, and the following conditions are met at the same time, 对象消失the problem (the object that should have been alive killed her!!!) problem: To
Insert picture description here
put it bluntly, the root cause is because the black object is not Will go back and scan again. For these two necessary conditions are provided two solutions: 增量更新和原始快照(SATB).

增量更新: It is to record when the reference is inserted, and rescan with these black objects as the root after the scanning is over. The incisive summary in "In-depth Understanding of the Java Virtual Machine": Simply understand that once a black object is newly inserted a reference to a white object, it turns back to a gray object .

原始快照:Also record the deleted reference objects, and rescan the gray object as the root after scanning. The same incisive summary: whether the reference relationship is deleted or not, it will be recorded according to the original object graph snapshot .

For the second question:
the virtual machine will certainly have to first create a new process in gc objects as alive, HotSpot designed two for each Region TAMS指针(Top Mark Start AT): prevTAMS和nextTAMS指针.

This part of "In-Depth Understanding of the Java Virtual Machine" is not clear, the following picture is more clear:
Insert picture description here
Note: The original picture link .

2.3 Operation process

It is roughly divided into four steps:

1. Initial mark. This stage is only simply marked to the part that can be directly associated with GC Roots. This stage needs to stop the thread but takes a short time.
2. Concurrent marking. Recursively scan the entire object graph for reachability analysis, which takes a long time but can be executed concurrently with user threads.
3. Final mark. Go back and rescan the legacy GC Roots in SATB (original snapshot). This process also needs to pause the thread.
4. Screening and recycling. Sort the collection according to the maximum revenue of the region's recycling, and use the mark-copy algorithm for garbage collection between the two regions.
Insert picture description here

Supplement: Root node enumeration, security point and security zone

We already know that the concurrency of the process of looking up the reference chain can be achieved through incremental scanning and original snapshot (SATB), but for the scanning of the root node, it is necessary to suspend the user thread . (If the object reference of the root node is also under constant modification, the analysis reliability of all subsequent results will not be guaranteed)

In order to improve the efficiency of the root node enumeration process, HotSpot will calculate and retain the reference information of the object after the class loading process (the reference relationship will also be recorded during the just-in-time compilation process).

安全点, In layman's terms, is where the user thread stops for garbage collection . Correspondingly 安全区域, it can be regarded as an elongated security point. The object reference relationship in this area will not change, so garbage collection can be performed at any time in this area . The existence of the safe zone can be used to solve the situation that the user thread is forced to stop due to various reasons (eg: Sleep state or Blocked state). If the virtual machine initiates garbage collection at this time, it will not take care of the users who declare that they are in the safe zone. Thread.

3. Comparison of G1 collector and CMS collector

First of all, the realization of the G1 collector was originally an epoch-making milestone. G1 performs memory layout according to Regions and implements the pause model. Users can specify the pause time by themselves, and sort the regions according to the recovery revenue to form a return collection . Secondly, G1 adopts a mark-organization algorithm as a whole, and a mark-copy algorithm in part, but no matter which is, the continuity of the memory space is guaranteed .
However, the amount of G1 generated for garbage collection 内存占用and program runtime is higher 执行负载than that of CMS.

Misfortune lies on the blessings

The G1 collector no longer strictly distinguishes between the new generation and the old generation, and uniformly adopts the memory structure of the Region. As far as the memory set is concerned, each Region corresponds to a card table (in contrast, CMS only records the objects from the old generation to the new generation. Reference. Because the new generation of object references change too frequently, this processing method is more cost-effective), and it is more complicated than the card table of CMS, which ultimately leads to the extra memory usage of G1 is much higher than that of CMS.

Guess you like

Origin blog.csdn.net/m0_46550452/article/details/110386219