One of the Java features is that you don’t need to explicitly manage the life cycle of objects: you can create objects as needed, and when they are no longer in use, they are automatically reclaimed by the JVM in the background. In simple terms, garbage collection consists of two steps: finding objects that are no longer used, and freeing the memory that those objects manage.
Garbage collection algorithm
Generation collection theory
In the Java VIRTUAL machine, the Java heap is divided into Young Generation and Old Generation regions. In the new generation, a large number of objects are found dead during each garbage collection, and the few objects that survive after each collection will be gradually promoted to the old age for storage.
There are two performance advantages to adopting this design:
- First, because the new generation is only part of the heap, it can be dealt with more quickly than the whole heap. This means shorter application thread pauses.
- Second, the object is allocated to Eden space. During garbage collection, the space of the new generation will be emptied, and objects in Eden’s space will either be removed or recycled. Since all objects are removed, the new generation space is automatically compressed during garbage collection.
The garbage collector collects only one or some of the regions at a time — hence the division of collection types such as “Minor”, “Major”, and “Full”
-
Partial GC: Garbage collection that does not aim to collect the entire Java heap.
-
Minor /Young GC: Garbage collection that targets only the new generation.
-
Major GC: Garbage collection that targets only Old GC. Currently, only the CMS collector has the behavior of collecting older generations separately.
-
Mixed GC: Garbage collection that aims to collect the entire new generation and parts of the old generation. Currently, only the G1 collector has this behavior.
-
Full GC: Collects the entire Java heap and method area garbage collection. This usually causes application threads to pause for a long time
Mark-clear algorithm
First, mark all objects that need to be reclaimed. After the mark is completed, all marked objects are uniformly reclaimed. Alternatively, mark surviving objects and uniformly reclaimed all unmarked objects. There are two main disadvantages:
- The first is that the execution efficiency is not stable. If the Java heap contains a large number of objects, and most of them need to be recycled, then a large number of marking and clearing actions must be carried out, resulting in the execution efficiency of both the marking and clearing process decreases with the increase of the number of objects.
- The second problem is the fragmentation of memory space. After marking and clearing, a large number of discontinuous memory fragments will be generated. Too much space fragment may cause that when large objects need to be allocated in the running process of the program, enough continuous memory cannot be found and another garbage collection action has to be triggered in advance.
Mark-copy algorithm
Divide the available memory into two equally sized pieces by capacity and use only one piece at a time. When this area of memory is used up, the surviving objects are copied to the other area, and the used memory space is cleaned up again. What the algorithm needs to copy is a small number of living objects, and each time the memory is reclaimed for the whole half region, so when allocating memory, there is no need to consider the complex situation of space fragmentation, just move the heap top pointer and allocate it in order.
Disadvantages: The mark-copy algorithm requires more copy operations when the object survival rate is high, which will reduce the efficiency. And it wastes 50% of the space. In the extreme case where all objects are 100% alive in the old age, this algorithm is generally not used in the old age.
Mark-collation algorithm
The marking process is still the same as the mark-clean algorithm, but instead of cleaning up the recyclable objects directly, the next step is to move all surviving objects to one end of the memory space, and then clean up the memory directly beyond the boundary.
Disadvantages: In The old days there were a lot of live object areas per collection, moving live objects and updating all references to them would be a very heavy operation, and The object movement had to Stop The World at all.
Classic garbage collector
Seven collectors are implemented in the HotSpot virtual machine for different generations.
If there is a line between two collectors, they can be used together, and the region of the collector in the figure indicates whether it belongs to a new generation or an old generation.
Clear a point: though we will carries on the comparison to the collector, but not to choose a best collector, although the garbage collector technology is improving, but until now is not the best collector, more do not exist “universal” collector, so we choose only the most suitable collector for specific applications.
Serial collector
The Serial collector is the most basic and oldest collector. It is a single-threaded collector. With Serial, all application threads are suspended when the heap space is cleared, whether for Minor or Full GC. When doing Full GC, it also compacts objects from the old chrono. The Serial collector can be enabled with the -xx :+UseSerialgGC flag.
For single-core processors or environments with a small number of processor cores, the Serial collector can achieve maximum single-thread collection efficiency by focusing on garbage collection because there is no overhead of thread interaction. The Serial collector is a good choice for virtual machines running in client mode.
ParNew collector
The ParNew collector is essentially a multithreaded parallel version of the Serial collector, in addition to using multiple threads simultaneously for garbage collection, The rest of The behavior includes all The control parameters available to The Serial collector, collection algorithms, Stop The World, object allocation rules, reclaim policies, and so on are exactly The same as The Serial collector.
The ParNew collector is by no means better than the Serial collector in a single-core processor environment, and even in a hyper-threading pseudo-dual-core processor environment, the collector is not 100 percent guaranteed to outperform the Serial collector because of the overhead of thread interaction. Is the preferred next-generation collector for legacy systems prior to JDK 7.
Parallel avenge
The Parallel Collector is a new generation collector based on the mark-copy algorithm. The Parallel Collector is a multi-threaded collector that can collect in Parallel very similar to ParNew.
The goal of the Parallel Insane is to achieve a controlled Throughput. Throughput is the ratio of the time the processor spends running user code to the total elapsed time of the processor. If the virtual machine completes a task and the user code plus garbage collection takes 100 minutes, of which garbage collection takes 1 minute, the throughput is 99%.
The Parallel Scavenge collector provides two parameters for precise throughput control, the -xx: MaxGCPauseMillis parameter, which controls the maximum garbage collection pause time, and the ** -xx: GCTimeRatio** parameter, which directly sets the throughput size.
- -xx: the MaxGCPauseMillis parameter allows a value greater than 0 milliseconds, and the collector will do its best to ensure that memory reclamation takes no longer than the user-set value. Garbage collection pauses are reduced at the expense of throughput and new generation space: The system made the new generation smaller, 300MB new generation is definitely faster than 500MB new generation, but this directly caused garbage collection to occur more frequently, instead of 10 seconds of 100 ms pauses, now 5 seconds of 70 ms pauses. Pause times did go down, but so did throughput. By default, this parameter is not specified.
- -xx: the value of the GCTimeRatio parameter should be an integer between 0 and 100. It is the ratio of the garbage collection time to the total time, which is the inverse of the throughput. For example, if this parameter is set to 19, the maximum allowed garbage collection time is 5% of the total time (i.e. 1/(1+19)). The default is 99, which is the maximum allowed garbage collection time of 1% (i.e. 1/(1+99)).
Another parameter is -xx: +UseAdaptiveSizePolicy. The vm collects performance monitoring information based on the current system running status and adjusts these parameters dynamically to provide the most appropriate pause time or maximum throughput. This type of tuning is called an adaptive tuning strategy for garbage collection (GERGonomics). Adaptive tuning strategies are also an important feature that distinguishes the collector from the ParNew collector.
Serial Old collector
Serial Old is an older version of the Serial collector, which is also a single-threaded collector using a mark-collation algorithm.
Parallel Old collector
Parallel Old is an older version of the Parallel Avenge collector, supported by multiple threads for concurrent collection and implemented on a mark-collation algorithm.
The default garbage collector in JDK8 is UseParallelGC or Parallel Insane + Parallel Old. Run the Java -xx :+PrintCommandLineFlags -version command to view the version information
java -XX:+PrintCommandLineFlags -version -XX:InitialHeapSize=266359616 -XX:MaxHeapSize=4261753856 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:-UseLargePagesIndividualAllocation -xx :+UseParallelGC Java version "1.8.0_66" Java(TM) SE Runtime Environment (build 1.8.0_66-b17) Java HotSpot(TM) 64-bit Server VM (Build 25.66-b17, Mixed mode)Copy the code
Concurrent Mark Sweep CMS collector
The CMS collector was designed to eliminate long pauses in the Parallel collector and Serial collector Full GC cycles. The CMS collector pauses all application threads during the Minor gc and performs garbage collection in a multithreaded manner. CMS collector based on tags – clear algorithm implementation, the whole process is divided into four steps, the longest of the whole process of concurrent tags and concurrent sweep phase, the garbage collector thread can be working with the user thread, so overall, CMS memory recovery process of the collector is concurrently with user thread.
CMS Initial Mark
The initial tag simply marks objects that GC Roots can be directly associated with, which is fast; Still need to “Stop The World”.
CMS Concurrent Mark
The concurrent marking phase is the process of traversing the entire object graph from the directly associated objects of GC Roots. This process is time-consuming but does not require the user thread to be paused and can be run concurrently with the garbage collection thread.
Re-marking (CMS Remark)
The relabeling phase corrects for concurrent tagging periods, where the pause time is usually slightly longer than the initial tagging phase, but also much shorter than the concurrent tagging phase.
CMS Concurrent sweep
Concurrent cleanup phase, which cleans and deletes dead objects judged by the marking phase, can also be concurrent with the user thread because there is no need to move the living objects.
The CMS collector is a collector whose goal is to obtain the shortest collection pause time. At present, a large part of Java applications are concentrated on the server side of Internet websites or B/S system based on browsers. Such applications usually pay more attention to the response speed of services and hope that the system pause time is as short as possible to bring good interactive experience to users. The CMS collector is a good fit for such applications.
Garbage First collector (G1)
The GI garbage collector is designed to minimize pauses when working with very large heaps (larger than 4GB). On the release date of JDK 9, G1 was declared to replace the Parallel Insane and Parallel Old combination as the default garbage collector in server mode, while CMS was reduced to a collector declared deprecated.
While G1 is still designed to follow the generational collection theory, its heap memory layout is very different from other collectors: G1 no longer insists on a fixed size and a fixed number of generational regions, but divides the continuous Java heap into multiple independent regions of equal size. Each Region can act as the Eden space of the new generation, Survivor space, or old chronospace as required. The collector can apply different strategies to regions that play different roles, so that both newly created objects and old objects that have survived multiple collections for a while can be collected well. Enable G1 garbage collection with the flag -xx :+UseG1GC (off by default).
The operation of the G1 collector can be roughly divided into four steps:
Initial Marking
Simply mark the objects that GC Roots can associate directly with, and change the value of the TAMS pointer so that the next phase of user threads running concurrently will allocate new objects correctly in the available regions. This phase requires the thread to pause, but it is very short, and it is done synchronously during the Minor GC, so the G1 collector actually has no additional pauses during this phase.
Concurrent Marking
Reachability analysis of objects in the heap is performed starting with GC Root, recursively scanning the entire heap object graph to find objects to reclaim, which is time-consuming but can be performed concurrently with the user program. When the object graph scan is complete, it is also necessary to reprocess objects recorded by SATB that have reference changes at the time of concurrency.
There is at least one (most likely multiple) generation garbage collection in a concurrent cycle. Therefore, before the partitions in the Eden room are marked for full release, the new Eden partition has already been allocated. These X partitions are old (they still hold data), and they are the partitions that the marking cycle found to contain the most garbage. In the marking cycle, the new generation of garbage collection is promoted to the old generation.
The marking cycle doesn’t actually free any objects from the old years: it locks only the most garbage-prone partitions. Garbage data in these partitions will be collected and released in subsequent cycles.
Final Marking
Another short pause is made on the user thread to process the last few SATB records that remain after the concurrent phase is over. All G1 really does up to this point is figure out which old partitions have the most garbage to recycle (that is, partitions labeled X).
Live Data Counting and Evacuation
G1 now performs a series of mixed GC. These garbage collections are called “hybrid” because they not only perform normal new generation garbage collection, but also reclaim some of the partitions marked by the background scanning thread.
As is common behavior for new generation garbage collection, the G1 collector has emptied the Eden space and resized the Survivor space. In addition, the two marked partitions have been reclaimed. These partitions have been confirmed to contain a large number of junk objects in previous scans, so most of them have been freed.
Memory allocation and reclamation policies
Memory allocation
Objects are allocated in Eden first
In most cases, objects are allocated in the Eden region of the new generation. When the Eden area does not have enough space to allocate, the virtual machine will initiate a Minor GC.
Big object goes straight to the old age
Large objects are Java objects that require a large amount of contiguous memory. The most typical large objects are long strings or arrays with large numbers of elements.
The reason to avoid large objects in the Java VIRTUAL machine is that when allocating space, it can cause garbage collection to be triggered in advance when there is plenty of memory available, in order to obtain enough contiguous space to place them, and when copying objects, large objects can mean high memory copying overhead.
The HotSpot VIRTUAL machine provides -xx: The PretenureSizeThreshold(valid only for Serial and ParNew collectors) parameter specifies that objects larger than this value are assigned directly to the older generation. The goal is to avoid copying back and forth between the Eden region and two Survivor regions. Generates a large number of memory replication operations.
Long-lived objects will enter the old age
The virtual machine defines an object Age counter for each object, which is stored in the object header. An object is usually born in Eden, and if it survives after the first Minor GC and can be accommodated by Survivor, it is moved to Survivor and its object age is set to 1. Each time an object survives a Minor GC in a Survivor zone, its age increases by one year, and when it reaches a certain age (15 by default), it is promoted to the old age. The age threshold for the object to be promoted to the old age can be set by using -xx: MaxTenuringThreshold.
The virtual machine does not always require the object’s age to reach MaxTenuringThreshold to be promoted to the old age. If the sum of the size of all objects of the same age in the Survivor space is greater than half of the size in the Survivor space, objects older than or equal to this age can directly enter the old age. No need to wait until the required age.
Space allocation guarantee
Before a Minor GC occurs, the virtual machine must first check that the maximum contiguous space available for the old generation is greater than the total space available for all objects of the new generation. If this condition is true, then the Minor GC is safe for this time. If no, check whether the -xx: HandlePromotionFailure parameter allows guarantee failure. If allowed, it will continue to check whether the maximum available contiguous space of the old age is greater than the average size of the objects promoted to the old age, and if so, a Minor GC will be attempted, although this Minor GC is risky; If less than, or the setting does not allow risk, then a Full GC should be performed instead.
Recovery strategy
Cenozoic collector
The new generation collectors such as Serial and ParNew of HotSpot VIRTUAL machine adopt the half-region copy generation strategy to design the memory layout of the new generation. The new generation is divided into one large Eden space and two small Survivor Spaces, and only Eden and one Survivor are used for memory allocation each time. When garbage collection occurs, the surviving objects in Eden and Survivor are copied to another Survivor space at once, and Eden and the used Survivor space are cleaned up directly. The default HotSpot VIRTUAL machine size ratio of Eden and Survivor is 8:1, that is, the available memory space of each new generation is 90% of the capacity of the whole new generation (80% of Eden plus 10% of a Survivor), and there is only one Survivor space. 10% of the new generation will go to waste.
Memory regions can be observed with JAVA VisualVM
Old age collector
The old generation garbage collection reclaims all objects in the new generation (including objects in Survivor Spaces). Only those objects that have active references, or that have been compressed (they occupy the beginning of the old age), will remain in the old age; the rest will be recycled.
Virtual machine and garbage collector logs
Each collector’s log format is determined by its own implementation; in other words, each collector’s log format can be different.
To view basic GC information: -xx: +PrintGC
To view GC details: -xx: +PrintGCDetails
-xx: +PrintHeapAtGC to view heap and method area available capacity changes before and after GC
Check the concurrent time and pause time of user threads during GC:
– XX: + Print – GCApplicationConcurrentTime
– XX: + PrintGCApplicationStoppedTime
The gc.log file is generated using -xloggc :D:/gc.log
2020-10-14T16:04:30.395+ 0800:0.160: [GC (system.gc ())] [PSYoungGen:9382K->1032K] 9382K->1040K(251392K), 0.0203927secs] [Times: User =0.00 sys=0.00, real= 0.02secs] 中 文 : 2020-10-14T16:04:30.416+ 0800:0.161: [PSYoungGen: 1032K->0K(76288K)] [ParOldGen: 8K->879K(175104K)] 1040K->879K(251392K), [Metaspace: Secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 2020-10-14T16:04:30.395+0800: 0.160: Current timestamp [GC (system.gc ())) triggers GC through system.gc ()] 9382K(used capacity of the memory region before GC)->1032K(used capacity of the memory region after GC)(76288K(total capacity of the memory region))] 9382K ->1040K, 0.0203927secs] [Times: 10382K ->1040K, 0.0203927secs] User =0.00 sys=0.00, real=0.02 secs] user: user CPU time sys: system CPU time real: actual time 2020-10-14T16:04:30.416+ 0800:0.161: [PSYoungGen: 1032K->0K] [PSYoungGen: 1032K->0K] 8K->879K(175104K)] 1040K->879K(251392K), [Metaspace: 3208K->3208K(1056768K)], 0.0041625secs] [Times: User sys = = 0.00 0.00, real = 0.00 secs]Copy the code
[GC (system.gc ())) /[Full GC (System.gc())) : Indicates the GC type and the GC trigger mode.
“[PSYoungGen”, “[ParOldGen”, “[Metaspace” indicate the region where GC occurs, and the name is also determined by the collector.
9382K->1032K(76288K) : Used capacity of the memory region before GC -> Used capacity of the memory region after GC (total capacity of the memory region)
9382K->1040K(251392K): Used capacity of the Java heap before GC -> Used capacity of the Java heap after GC
You can also use the GCViewer tool to analyze GC logs to perform relative JVM tuning.
Vm performance monitoring tool
Summary of Basic Tools
reference
The Java Performance Guru’s Guide To Understanding the Java Virtual Machine in Depth: Advanced JVM features and Best Practices (version 3)