Memory structure of the JVM

In a VM, different objects live for different periods of time. To distinguish objects of different ages and better manage them, the generation algorithm is used in most VMS.

In the JVM, memory is divided into three segments: new generation, old generation, and persistent generation. As shown in figure:

Among them, Cenozoic is divided into Eden, Survivor0 and Survivor1. Why should it be divided into three zones? It starts with the GC algorithm.

Common GC algorithms

  • Mark-clear

    Mark-clearing is the most basic collection algorithm. The mark-clearing is divided into two stages: the first stage is to mark the objects to be recycled, and after the completion of the marking, all the marked objects are recycled uniformly. The disadvantages of tag clearing are obvious, that is, it will generate a large amount of memory fragmentation, at the same time, tag clearing is not efficient. As shown in figure:

  • Replication algorithm

    In order to solve the efficiency problem, the replication algorithm appears. The replication algorithm, as its name implies, copies objects that have no references to another block of memory and then reclaims the used memory. The advantage of this method is that you no longer have to worry about fragmentation, but the disadvantage is that you can only use one piece of memory at a time after splitting it into two pieces. As shown in figure:

    In business practice, it is found that more than 98% of the objects in the new generation die at the same time. There is no need to divide the memory space according to 1:1. Generally, the memory space is divided into one large Eden area and two small Survivor areas, and only Eden area and one Survivor area are used each time. When memory reclamation is performed, the Eden region and the surviving objects of one Survivor are copied to another Survivor, and the Eden region and Survivor region are cleaned up.

    In the current Hotspot virtual machine, the ratio of Eden zones to Survivor is 8:1, so that at most 1/10 of memory is wasted at any one time, avoiding 1/2 of memory being idle.

  • Tag to sort out

    Although the replication algorithm solves the memory fragmentation problem, if the objects are not dead, the replication algorithm has to move the surviving objects around every time, which is very inefficient. Another point is that we use the copy algorithm in the new generation, but not all objects can be allocated in the new generation, often some large objects may need to be allocated directly in the old age.

    Therefore, in the old days, the copy algorithm was not used, but the mark-collation algorithm was used.

    Mark-clean is the same as the mark-clean algorithm, but it takes a step further than mark-clean by moving the surviving objects around, putting them together, and then clearing them into another space. As shown in figure

Generational collection

Currently, all commercial VMS use generational collection algorithms. Based on the life cycle of objects, the memory is divided into several blocks, such as the old generation and the new generation. Different collection algorithms can be used based on the characteristics of different ages. In practice, generally the new generation adopts the copy algorithm, and the old generation adopts the mark-collation algorithm, or the mark-clean algorithm.

ParNew collector

The ParNew garbage collector is an implementation of the replication algorithm described above. Its predecessor is Serial, which is known by its name as the single-thread collector. It works as follows:

Serial uses a single GC thread. It is simple, efficient, and easy to implement. Currently, Serial is the default GC collector in Client mode.

In contrast to Serial, the ParnNew collector is the multithreaded version, which can greatly reduce the collection time. As shown in figure:

At present, the mainstream CMS collector is used by default in the new generation of ParnNew collector, today is to talk about the main content of ParnNew in the new generation of tuning attention to the problem, before adjusting, first look at a case.

case

On the HBase server in our online environment, the default configuration used by o&M engineers when I was not involved had the following problems in GC logs.

What’s wrong with logs?

[Times: user=110.02, sys=0.02, real-3.59secs] [Times: user=110.02, sys=0.02, real-3.59secs] [Times: user=110.02, sys=0.02, real-3.59secs] Looking at the logs carefully, every time yong GC is performed, data is directly entered into O zone. Some objects that should have been in Young zone are dropped into O zone by GC, resulting in the increase of Old zone and triggering of FullGC, which seriously affects the performance of application services.

Cause analysis,

In a new collector, the default ratio is 8:1:1. This is achieved with the parameter — XX:SurvivorRatio.

At each Minor GC, the collector copies Eden and any surviving objects from one S-region to the other. The default ratio of 8 is OK, and more than 98% of objects die overnight. The entire GC process is as follows:

Let’s start with some age.

In the JVM, each time an object undergoes a Minor GC, its age is incremented by one, and after reaching the “promotion age threshold”, it is placed in the old age, a process also known as “promotion”. Obviously, the “promotion age threshold” directly affects the residence time of the object in the new generation. In Serial and ParNewGC, the “promotion age threshold” is set by the parameter MaxTenuringThreshold. The default value is 15.

Age other reasons affecting promotion

MaxTenuringThreshold = 15, age=1, age=1, age=1, age=1, age=1

It turns out that in Hotspot, dynamic ages are used to determine whether an object advances to the old age.

Dynamic age calculation: As Hotspot traverses all objects, it accumulates the size occupied by them by age from small to large. When it accumulates a certain age that exceeds half of the survivor zone, it takes this age and a smaller value of MaxTenuringThreshold as the new promotion age threshold. In this case, before tuning: The Survivor zone = 64M, desired Survivor = 32M, at which point the cumulative size of objects in the Survivor zone with age<=2 is 41M, and 41M is larger than 32M, so the promotion age threshold is set to 2, and the objects older than 2 are promoted to the old age in the next Minor GC.

The JVM introduced dynamic age calculations based on two considerations:

If the threshold set by MaxTenuringThreshold is fixed as the promotion condition:

  • If MaxTenuringThreshold is set too large, the objects that should be promoted will stay in Survivor zone until the Survivor zone overflows. Once the overflow occurs, the objects in Eden+Svuvivor will no longer be promoted to the old age according to their ages, so the mechanism of object aging will be invalid.
  • If MaxTenuringThreshold is set too small, “premature promotion” means that objects cannot be fully recovered in the new generation. A large number of short-term objects are promoted to the old age, and the old age space grows rapidly, leading to frequent Major GC. Generational collection is meaningless and seriously affects GC performance.

The performance of the same application varies at different times: the execution of special tasks or changes in traffic components will lead to fluctuations in the life cycle distribution of objects. Therefore, a fixed threshold setting will cause the same problems as above because it cannot dynamically adapt to changes.

To sum up, virtual machines do not always require objects to reach a Maxtenuringthreshhold age before advancing to the old age in order to better accommodate the memory requirements of different applications.

Another case is when the objects generated in the Young section are too large, we can choose to go straight to the old age. Serial and ParNew provide a – XX: PretenureSizeThreadhold parameters, make greater than this parameter value distribution of objects directly in the old era, avoid in Eden area and two Survivor area copy a lot of memory.

Back to the case

Through the above analysis, we can know the problems in the log, because in the new generation, the memory of age=1 exceeds half of S area, and the object is directly promoted to the old age. In view of this imagination, we can increase the space of S area and reduce Eden’s area by adjusting the size of SurvriorRatio. This allows the object to survive several GC cycles before advancing to the old age. In addition to adjusting the SurvriorRatio, GC can also be improved by adjusting the size of Young zone in Cenozoic.

New performance adjustment mode

To sum up, the performance optimization methods of the New generation are as follows:

  • Young size adjustment
  • The adjustment of the SurvriorRatio
  • Age Settings for promotion
  • Set the number of GC threads