Detailed explanation of G1 GC garbage collector

G1 GC is one of the new features of Jdk7, and Jdk7+ versions can configure G1 as a JVM GC option; as a major upgrade of the JVM GC algorithm, G1 has been relatively stable after DK7u, and plans to replace CMS in the future, so it is necessary to have an in-depth understanding :

Unlike other generational collection algorithms, G1 divides the heap space into independent blocks. Each area may belong to either the O area or the Y area, and the space of each type of area can be discontinuous (compare the O area and the Y area of ​​the CMS must be continuous). This idea of ​​dividing the O region into multiple chunks stems from the fact that when concurrent background threads are looking for recyclable objects, some chunks contain much more recyclable objects than others. Although G1 still needs to suspend the application thread when cleaning up these blocks, it can preferentially reclaim the blocks containing the most garbage in relatively less time. This is why G1 is named Garbage First: the blocks with the most garbage are processed first.

 

Most systems in normal work use CMS. Even if silently upgraded to JDK7, CMS is still used by default. The difference between G1 and CMS is:

  1. G1 has advantages in compressing space
  2. G1 avoids memory fragmentation by dividing the memory space into regions
  3. Eden, Survivor, Old areas are no longer fixed and more flexible in terms of memory usage efficiency
  4. G1 can control the garbage collection time to avoid application avalanche by setting the expected pause time (Pause Time)
  5. G1 will do the work of merging free memory immediately after reclaiming memory, while CMS does it at STW (stop the world) by default.
  6. G1 will be used in Young GC, while CMS can only be used in O area

For now, CMS is still the default preferred GC strategy, and G1 may be more suitable in the following scenarios:

  1. Server-side multi-core CPUs and applications with large JVM memory usage (at least greater than 4G)
  2. The application will generate a lot of memory fragmentation during the running process, and the space needs to be compressed frequently
  3. Want a more controllable and predictable GC pause cycle; prevent application avalanches under high concurrency

A detailed process of a complete G1GC:

G1 mainly includes the following 4 operation modes during operation:

  1. YGC (different from CMS)
  2. concurrent stage
  3. blend mode
  4. full GC (usually occurs when there is a problem with G1)

YGC :

The following is a schematic diagram of the memory area before and after a YGC:

G1-1

G1-2

Each small block in the figure represents a region (Region) of G1, and the letters in the block represent blank blocks of different generational memory space types (such as [E]Eden, [O]Old, [S]Survivor). Does not belong to any partition; G1 can arbitrarily designate this area to belong to Eden or O area when needed.
G1 YoungGC is triggered when Eden is full, and all blocks that previously belonged to Eden become empty after recycling. Then at least one block belongs to the S area (the half-full area in the figure), and some data may be moved to the O area.

At present, most of the Tao series applications use the PrintGCDetails parameter to print the GC log. This parameter is also valid for G1, but the log content is quite different; the following is an example of Young GC:

23.430: [GC pause (young), 0.23094400 secs]
...
[Eden: 1286M(1286M)->0B(1212M)
Survivors: 78M->152M Heap: 1454M(4096M)->242M(4096M)]
[Times: user=0.85 sys=0.05, real=0.23 secs]

Analysis of the content of the above log: Young GC actually takes 230 milliseconds, of which the GC thread takes 850 milliseconds of CPU time
E: The memory usage has changed from 1286MB to 0, and it has been moved out
S: It has grown from 78M to 152M, indicating that 74M
Heap was moved from Eden : The occupancy has changed from 1454 to 242M, indicating that the Young GC has released a total of 1212M of memory space.
In many cases, the objects in the S area will be partially promoted to the Old area. In addition, if the S area is full, the objects that survive in Eden will be directly promoted to Old area, in this case, the space of Old will increase

Concurrent stage:

The memory usage before and after a concurrent G1 recycling cycle is shown in the following figure:

G1-3

G1-4

The following points can be seen from the above chart:
1. The Young area has changed, which means that at least one YGC has occurred in the G1 concurrent phase (this is different from CMS), and Eden has been completely emptied before marking , because the application thread is working at the same time in the concurrent phase, so you can see that Eden has new occupation
2. Some areas are marked by X, these areas belong to the O area, there is still data storage at this time, the difference is marked in G1 These areas contain the most garbage, that is, the area with the highest recycling yield
3. After the concurrent phase is completed, the capacity of the O area actually becomes larger (O+X blocks). At this time, it is caused by the entry of new objects in YGC during this process. In addition, this phase does not reclaim any objects in the O area: its role is mainly to mark out the blocks with the most garbage. Objects actually start to be collected at a later stage

The G1 concurrent marking cycle can be divided into several phases, some of which require the application thread to be suspended. The first stage is the initial marking stage. This phase suspends all application threads - partly because the process performs a YGC. Here is an example log:

50.541: [GC pause (young) (initial-mark), 0.27767100 secs]
[Eden: 1220M(1220M)->0B(1220M)
Survivors: 144M->144M Heap: 3242M(4096M)->2093M(4096M)]
[Times: user=1.02 sys=0.04, real=0.28 secs]

The above log shows that a YGC occurred, the application thread was suspended for 280 milliseconds, and the Eden area was emptied (71MB was moved from the Young area to the O area).
The word initial-mark in the log indicates that the concurrent GC phase in the background has started. Because the initial marking phase itself also needs to suspend the application thread,
G1 happens to do this together in the process of YGC. The extra overhead for this is not very large, and the CPU increases by 20%, and the pause time is correspondingly slightly longer.

Next, G1 starts scanning the root zone, log example:

50.819: [GC concurrent-root-region-scan-start]
51.408: [GC concurrent-root-region-scan-end, 0.5890230]

It took a total of 580 milliseconds, and the process did not suspend the application thread; it was processed in parallel by the background thread. This phase cannot be interrupted by YGC, so it is critical that background threads have enough CPU time. If the Young area is
full during the root scan, YGC must wait for the root scan before proceeding. The impact is that the YGC pause time will increase accordingly. The GC log at this time is as follows:

350.994: [GC pause (young)
351.093: [GC concurrent-root-region-scan-end, 0.6100090]
351.093: [GC concurrent-mark-start],0.37559600 secs]

It can be seen that the GC pause occurred before the root scan ended, indicating that the YGC waited, and the waiting time was about 100 milliseconds.
After the root scan is complete, G1 enters a concurrent marking phase. This phase also takes place entirely in the background; the following messages in the GC log represent the beginning and end of this phase:

111.382: [GC concurrent-mark-start]
....
120.905: [GC concurrent-mark-end, 9.5225160 sec]

The concurrent marking phase can be interrupted, for example, if YGC occurs during this process. This phase will be followed by a secondary marking phase and cleaning phase:

120.910: [GC remark 120.959:
[GC ref-PRC, 0.0000890 secs], 0.0718990 secs]
[Times: user=0.23 sys=0.01, real=0.08 secs]
120.985: [GC cleanup 3510M->3434M(4096M), 0.0111040 secs]
[Times: user=0.04 sys=0.00, real=0.01 secs]

These two phases also suspend the application thread, but for a short time. Then there is an additional concurrent cleanup phase:

120.996: [GC concurrent-cleanup-start]
120.996: [GC concurrent-cleanup-end, 0.0004520]

At this point, a normal G1 cycle has been completed – what this cycle mainly does is to discover which areas contain the most garbage (marked as X) that can be collected, and less space is actually released.

Mixed GC:

Next G1 performs a series of mixed GCs. This period is called the mixed phase because YGC and cleaning of the area marked with X will be performed at the same time. The following is a schematic diagram before and after the execution of mixed GC:

G1-5

G1-6

Like ordinary YGC, G1 completely clears Eden and adjusts the survivor area. In addition, the two markers are also reclaimed. They have a common feature of containing the most recyclable objects, so the absolute part of the space of these two areas is released. Any surviving objects in these two areas are moved to other areas (similar to the promotion of YGC surviving objects to the O area). This is why the G1 heap is much less fragmented than the CMS memory - moving these objects is also compacting the memory. Here is the log of a mixed GC:

79.826: [GC pause (mixed), 0.26161600 secs]
....
[Eden: 1222M(1222M)->0B(1220M)
Survivors: 142M->144M Heap: 3200M(4096M)->1964M(4096M)]
[Times: user=1.01 sys=0.00, real=0.26 secs]

The above log can notice that Eden has released 1222MB, but the space released by the entire heap is larger than this amount. The number difference seems to be relatively small, only 16MB, but it should be considered that objects in the survivor area are promoted to the O area; in addition, each mixed GC only clears a part of the O area memory, and the entire GC will continue until almost all the marked area garbage The objects are all recycled, and G1 will return to the normal YGC phase after this phase is over. Periodically, when the memory usage of the O area reaches a certain amount, G1 will start a new parallel GC phase.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326285112&siteId=291194637