1. The classification of GC

  • Garbage collectors are not specified in the JVM specification and can be implemented by different manufacturers and versions of JVMS
  • Due to the rapid iteration of JDK versions, Java has spawned numerous GC versions to date
  • By analyzing the garbage collector from different perspectives, GC can be categorized into different types
    • According to theThe number of threadsCan be divided intoSerial garbage collectorandParallel garbage collector
      • Serial collection means that only one CPU is allowed to perform garbage collection at a time, at which point the worker thread is suspended until the garbage collection is complete
        • In cases where hardware platforms are not particularly superior, such as single-CPU processors or small application memory, serial collectors can outperform parallel and concurrent collectors. So serial reclamation is applied by default to the JVM in Client mode on the Client side
        • Parallel collectors produce shorter pause times than serial collectors on more concurrent cpus
      • Parallel collector, on the other hand, can use multiple cpus to perform garbage collection at the same time, thus improving the throughput of the application, but parallel collection is still the same as serial collection, using an exclusive STW mechanism

    • According to theWorking modeCan be divided intoAnd deploy the garbage collectorandExclusive garbage collector
      • And the garbage collector works alternately with application threads to minimize application pause time
      • Once running, the exclusive garbage collector (STW) stops all user threads in the application until the garbage collection process is complete
    • According to theDebris handling modeCan be divided intoCompressed garbage collectorandNon-compressed garbage collector
      • The compressed garbage collector compresses the surviving objects after the collection is complete to eliminate the recovered fragments
        • Redistribute object space using: pointer collisions
        • Reallocate object space usage: free list
      • Non-compressed garbage collectors do not do this
    • According to the working memory interval, it can be divided into young generation garbage collector and old generation garbage collector

2. Evaluate GC performance metrics

  • Throughput: The amount of time spent running user code as a percentage of total elapsed time (total elapsed time: the elapsed time of the program + the elapsed time of memory reclamation)
  • Pause time: The amount of time a program’s worker thread is suspended while garbage collection is being performed
  • Garbage collection overhead: recovery of throughput, ratio of garbage collection time to total run time
  • Memory footprint: The amount of memory occupied by the Java heap
  • Collection frequency: How often collection operations occur relative to the execution of the application
  • Fast: the time an object takes from birth to being recycled,

Throughput, pause time, and garbage collection overhead form an impossible triangle. The overall performance of all three will get better and better as technology advances. A good collector usually does at most two of these. Of these three, “pause times” become increasingly important because as hardware evolves, memory footprint becomes more and more tolerable, and increased hardware performance also helps reduce the impact of collector runs on the application, thereby increasing throughput. Memory expansion has a negative effect on latency.

In short, there are two main points to focus on: throughput and pause times

Throughput

  • Throughput is the ratio of CPU time spent running user code to total CPU consumption, i.e. Throughput = user code time run/(user code time run + garbage collection time). For example, if the virtual machine runs for 100 minutes and garbage collection takes 1 minute, the throughput is 99%
  • In this case, applications can tolerate higher pause times, so high-throughput applications have a longer time baseline than fast responsiveness
  • Throughput first, which means that STW has the shortest time per unit time

Pause time

  • Pause time is the state during which the application thread pauses to allow the GC thread to execute. For example, a pause time of 100 milliseconds during GC means that no application threads are active during that 100 milliseconds
  • Pause time priority means keeping the time of a single STW as short as possible

Throughput vs pause time

  • High throughput is good because it gives the end user of the application the impression that only the application threads are doing “productive” work. Intuitively, the higher the throughput, the faster the application will run
  • Low pause times (low latency) are better because from the end user’s point of view, it is always bad for an application to be suspended, whether for GC or other reasons. Depending on the type of application, sometimes even a brief 200-millisecond pause can interrupt the end-user experience. Therefore, having low, large pause times is very important, especially for an interactive application
  • Unfortunately, “high throughput” and “low pause times” are a bunch of competing goals.
    • This is because if you choose throughput first, you will necessarily need to reduce the efficiency of the collection, but this will result in the GC needing longer pause times to perform the collection
    • On the contrary, if the principle of low latency first is selected, then in order to reduce the pause time of each memory reclamation, memory reclamation can only be performed frequently, but this causes the reduction of young generation memory and leads to the decrease of program throughput
  • When designing (or using) GC algorithms, we must determine our goals: a GC algorithm can only target one of two goals (i.e. focus only on large throughput or minimal pause times), or try to find a compromise between the two.
  • Now standard: Reduce pause times when maximum throughput is first

3. Overview of different garbage collectors

History of garbage collector

With virtual machines, there is a need for mobile phone Garbage. This is Garbage Collection, and the corresponding product is called Garbage Collector

  • In 1999, the Serial GC came with JDK1.3.1, the first of its kind, and the ParNew garbage collector is a multithreaded version of the Serial collector
  • Parallel GC and Concurrent Mark Sweep (CMS) GC were released along with JDK1.4.2 on February 26, 2002
  • Parallel GC became the default GC for HotSpot after JDK6
  • In 2012, G1 was available in JDK 1.7U4
  • In 2017, G1 became the default garbage collector in JDK9, replacing CMS
  • In March 2018, the G1 collector’s parallel full garbage collection approach was implemented in JDK10. Parallelism improves worst-case latency
  • In September 2018, JDK11 was released, introducing the Epsilon garbage collector, also known as the “no-OP” collector. At the same time, ZGC was introduced. Scalable low Latency garbage Collector (Experimental)
  • In March 2019, JDK12 was released with enhanced G1 that automatically returns unused heap memory to the operating system. Shenandoah GC was introduced. Low pause time GC (Experimental)
  • In September 2019, JDK13 was released to enhance ZGC and automatically return unused heap memory to the operating system
  • In March 2020, JDK14 was released, removing the CMS garbage collector and extending ZGC on macOS and Windows

Seven classic garbage collectors

  • Serial collector: Serial, Serial Old
  • Parallel collector: ParNew, Parallel Avenge, Parallel Old
  • Concurrent collector: CMS, G1

The official document: www.oracle.com/technetwork…

Among them:

  • Cenozoic collectors: Serial, ParNew, Parallel Insane
  • Collector: Serial Old, Parallel Old, CMS
  • Whole heap collector: G1

The composition of the garbage collector

  • There is a line between the two collectors, indicating that they can be used together: Serial/Serial Old, Serial/CMS, ParNew/Serial Old, ParNew/CMS, Parallel Scavenge/Serial Old, Parallel Scavenge/Parallel Old, G1
  • Serial Old is the backup plan for Concurrent Mode Failure of CMS
  • (red dotted line) Due to maintenance and compatibility testing costs, the combination Serial+CMS and ParNew + Serial Old was declared deprecated in JDK8 (JEP 173) and completely unsupported in JDK9 (JEP214), namely removed
  • Insane and Serial Old GC. (JEP 366)
  • (blue dotted line) Delete CMS garbage collector (JEP 363) in JDK14

Why do you need different garbage collectors

  • Because Java is used in many scenarios, mobile terminal, server, etc. Therefore, it is necessary to provide different garbage collectors for different scenarios to improve the performance of garbage collection
  • Although we will compare each collector, we are not trying to pick the best one. There is no one-size-fits-all, one-size-fits-all collector, and there is no one-size-fits-all collector. So we chose only the collector that was most appropriate for the specific application

How do I view the default garbage collector

  • -XX:+PrintCommandLineFlags: View command-line parameters (including the garbage collector used)
  • Using command line commands:jinfo -flagAssociated garbage collector parameter process ID

Code demo

package com.nasuf.jvm;

import java.util.ArrayList;

/** * -XX:+PrintCommandLineFlags */
public class GCUseTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        while (true) {
            byte[] arr = new byte[100];
            list.add(arr);
            try {
                Thread.sleep(10);
            } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

In the JDK8 environment, the output is as follows:

-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParallelGC 
Copy the code

-xx :+UseParallelGC indicates that the new generation is using the Parallel Old GC.

You can also view it using command line arguments:

$ jps
76310
51239 Jps
51176 GCUseTest
51177 Launcher
31546
32239 MacLauncher

$ jinfo -flag UseParallelGC 51176
-XX:+UseParallelGC

$ jinfo -flag UseParallelOldGC 51176
-XX:+UseParallelOldGC

$ jinfo -flag UseG1GC 51176
-XX:-UseG1GC
Copy the code

As you can see, the Parallel GC and Parallel Old GC are being used instead of the G1 GC

In the JDK9 environment, the output is as follows:

-XX:G1ConcRefinementThreads=8 -XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:ReservedCodeCacheSize=251658240 -XX:+SegmentedCodeCache -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseG1GC 
Copy the code

You can see that -xx :+UseG1GC indicates that G1GC is being used; You can also view it using command line arguments:

$ jps
52133 Jps
76310
52104 Launcher
52105 GCUseTest
31546
32239 MacLauncher

$ jinfo -flag UseParallelOldGC 52105
-XX:-UseParallelOldGC

$ jinfo -flag UseParallelGC 52105
-XX:-UseParallelGC

$ jinfo -flag UseG1GC 52105
-XX:+UseG1GC
Copy the code

4. Various garbage collectors are introduced

4.1 Serial collector: Serial collection

  • The Serial collector is the most basic and oldest garbage collector. The only option to recycle the new generation before JDK1.3
  • The Serial collector is the default new generation garbage collector in Client mode in HotSpot
  • The Serial collector performs memory collection using the replication algorithm, Serial collection, and STW mechanism
  • In addition to the young generation, the Serial collector also provides the Serial Old collector for performing the Old generation garbage collection.The Serial Old collector uses the same Serial collection and STW mechanisms, but the memory collection algorithm uses a mark-compression algorithm
    • Serial Old is the default Old garbage collector running in Client mode
    • Serial Old is used in Server mode as a Paralle Scavenge avenge. The second is as a backup garbage collection solution to the older CMS collector

  • The collector is a single-threaded collector, but its “single-threaded” meaning is not only that it uses only one CPU or one collection thread to complete the garbage collection, but also that it must suspend all other worker threads while it is garbage collection (STW).
  • Advantages: Simple and efficient (compared to the single-threaded collection of other collectors). For a single-CPU-limited environment, the Serial collector can achieve the highest single-threaded collection efficiency because it has no overhead of thread interaction; A virtual machine running in Client mode is a good choice
  • In a user’s desktop application scenario, the available memory is generally small (tens or even one or two hundred MB), garbage collection can be completed in a relatively short time (tens or even more than one hundred ms), and serial collector is acceptable as long as it does not occur frequently
  • In the HotSpot VIRTUAL machine, use-XX:+UseSerialGCArguments can specify that both young and old generations use serial collectors; This is equivalent to Serial GC for freshmen and Serial Old GC for seniors
  • This kind of garbage collector is well known, but it is no longer serial. And in the limited single-core CPU can only be used, now are not single-core operation. This garbage collector is unacceptable for highly interactive applications, and serial garbage collectors are generally not used in Java Web applications

4.2 ParNew collector: Parallel collection

  • If the Serial GC is a single-threaded garbage collector in the younger generation, the ParNew collector is a multithreaded version of the Serial collector
    • Par is short for Parallel, and New indicates that only the New generation can be processed
  • There is little difference between the two garbage collectors except that the ParNew collector performs memory collection in a parallel collection manner. The ParNew collector also uses the copy algorithm, STW mechanism, in the younger generation
  • ParNew is the default garbage collector for the new generation of many JVMS running in Server mode

  • For the new generation, the recycling times are frequent and the parallel method is efficient
  • For the old era, the number of recycling is less, and the serial method is used to save resources (CPU parallelism needs to switch threads, which can save resources for switching threads).
  • Since the ParNew collector is based on parallel collection, is it safe to assume that the ParNew collector will be more efficient at collecting than the Serial collector in any scenario?
    • ParNew collector runs in a multi-CPU environment. Because it can take full advantage of physical hardware resources such as multi-CPU and multi-core, it can complete garbage collection more quickly and improve the throughput of the program
    • However, on a single CPU, the ParNew collector is no more efficient than the Serial collector. Although the Serial collector is based on Serial collection, because the CPU does not need to switch tasks frequently, it can effectively avoid some of the extra overhead associated with multithreaded interactions
  • In addition to Serial, only ParNew GC currently works with the CMS collector
  • In the program, you can select-XX:+UseParNewGCManually specifying the use of the ParNew collector for memory reclamation tasks indicates that the younger generation uses the parallel collector without affecting the older generation
  • -XX:ParallelGCThreadsLimit the number of threads. By default, enable the same number of threads as the number of cpus

The test code

package com.nasuf.jdk8;

import java.util.ArrayList;

/** * -XX:+PrintCommandLineFlags -XX:+UseParNewGC */
public class GCUseTest {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        while (true) {
            byte[] arr = new byte[100];
            list.add(arr);
            try {
                Thread.sleep(10);
            } catch(InterruptedException e) { e.printStackTrace(); }}}}Copy the code

In the JDK8 environment, the output is as follows:

-XX:InitialHeapSize=268435456 -XX:MaxHeapSize=4294967296 -XX:+PrintCommandLineFlags -XX:+UseCompressedClassPointers -XX:+UseCompressedOops -XX:+UseParNewGC 
Java HotSpot(TM) 64-Bit Server VM warning: Using the ParNew young collector with the Serial old collector is deprecated and will likely be removed in a future release
Copy the code

In the JDK9 environment, the output is as follows:

/Library/Java/JavaVirtualMachines/jdk-9.01..jdk/Contents/Home/bin/java -XX:+PrintCommandLineFlags -XX:+UseParNewGC -Dfile.encoding=UTF-8 -classpath /Users/nasuf/Project/JvmNotesCode/out/production/jdk19.-module com.nasuf.jdk9.GCUseTest
Java HotSpot(TM) 64-Bit Server VM warning: Option UseParNewGC was deprecated in version 9.0 and will likely be removed in a future release.
It is not possible to combine the ParNew young collector with any collector other than CMS.
Error: Could not create the Java Virtual Machine.
Error: A fatal exception has occurred. Program will exit.
Copy the code

4.3 Parallel Scavenge: Throughput first

  • The younger generation of HotSpot, in addition to having the ParNew collector based on Parallel recycling, also uses the replication algorithm, Parallel recycling, and STW mechanism
  • Unlike the ParNew collector, the Parallel Scavenge collector aims to achieve a manageable Throughput. It is also known as a throughput-first garbage collector
  • The adaptive adjustment strategy is also an important distinction between the Parallel Avenge and ParNew
  • High throughput can efficiently use THE CPU time, as soon as possible to complete the operation of the program, mainly suitable for the background operation without too much interaction. Therefore, it is commonly used in server environments. Examples are applications that perform batch processing, order processing, payroll, and scientific calculations
  • Parallel collector A Parallel Old collector for performing old-era collections was provided in JDK6 to replace the Serial Old collector for old-era collections
  • The Parallel Old collector uses a mark-compression algorithm, but is also based on Parallel collection and STW mechanisms

  • In application throughput first scenarios, the combination of Parallel collector and Parallel Old collector performs well in Server mode memory reclamation
  • In Java8, the default is this garbage collector

Parameter configuration:

  • -XX:+UseParallelGC: Manually specifies the young generation to use the Parallel collector to perform memory reclamation tasks
  • -XX:+UseParallelOldGC: Manually specifies that older generations use a parallel garbage collector
  • One of the above two parameters is enabled by default, and the other is also enabled (mutual activation).
  • -XX:ParallelGCThreads: Set the number of threads for the young generation parallel collector, generally preferably equal to the number of cpus, to avoid excessive threads affecting garbage collection performance; By default, ParallelGCThreads is equal to the number of cpus when the number of cpus is less than eight; When the number of cpus is greater than 8, ParallelGCThreads has a value equal to3 + (5 * CPU_Count)/8
  • -XX:MaxGCPauseMillis: Sets the maximum garbage collector pause time (that is, the time of STW). Units are seconds
    • To keep the pause time within MaxGCPauseMillis as much as possible, the collector adjusts the Java heap size or some other parameter as it works
    • For users, the shorter the pause, the better the experience. But on the server side, we focus on high concurrency, overall throughput. So the server side is suitable for Parallel control
    • Use this parameter with caution
  • -XX:GCTimeRatio: Ratio of garbage collection time to total time(= 1 / (N + 1))Used to measure throughput size
    • Value range0 ~ 100, the default value is99, that is, the garbage collection time does not exceed1%
    • With the former one-XX:MaxGCPauseMillisParameters have certain contradictions. The longer the pause time, the Ratio parameter will easily exceed the set Ratio
  • -XX:+UseAdaptiveSizePolicyTo set up a Parallel collector with an adaptive adjustment strategy (The default open)
    • In this mode, parameters such as the size of the young generation, the ratio of Eden to Survivor, and the age at which older objects are promoted are automatically adjusted to achieve a balance between heap size, throughput, and pause time
    • In cases where manual tuning is difficult, you can use this adaptive approach to specify only the maximum heap of the virtual machine, the throughput of the target (GCTimeRatio), and the pause time (MaxGCPauseMillis), and let the virtual machine do the tuning itself

4.4 CMS collector: Low latency

  • In the JDK1.5 era, HotSpot introduced a garbage collector that could almost be considered revolutionary in strongly interactive applications: CMS(Concurrent-mark-sweep) collector, the first truly Concurrent collector in the HotSpot VIRTUAL machine, enables the garbage collector thread to work simultaneously with the user thread for the first time
  • The FOCUS of the CMS collector is to minimize the pause time of user threads during garbage collection. Shorter pause times (low latency) are more appropriate for applications that interact with the user, and better response times improve the user experience
    • At present, a large part of Java applications are concentrated on the server side of Internet sites or B/S systems. These applications pay special attention to the response speed of services and hope that the system pause time is the shortest, so as to bring users a better experience. The CMS collector is a good fit for such applications
  • CMS’s garbage collector algorithm uses a mark-sweep algorithm and also STW
  • Unfortunately, CMS, as a collector for older ages, does not work with the Parallel Scavenge collector, which already exists in JDK1.4. So when using CMS to collect older ages in JDK1.5, the new generation collector has to choose either ParNew or Serial
  • Before G1, CMS was widely used. Even today, many systems use the CMS GC

  • The whole process of CMS is more complex than the previous collector. The whole process is divided into four main stages, namelyInitial marking stage,Concurrent marking phase,Relabeling stageandConcurrent cleanup phase
    • Initial Mark: In this stage, all worker threads in the program are temporarily suspended due to the STW mechanism. The main tasks in this stage are only objects that GC Roots can be directly associated with at the Mark. Once the tag completes, all application threads that were suspended will be resumed. Because the directly related object is small, the speed here is very fast
    • Concurrent Mark: The process of traversing the entire object graph starting with objects directly associated with GC Roots. This process is time-consuming but does not require suspending the user thread and can be run concurrently with the garbage collection thread
    • Remark Phase: Because in concurrent mark phase, the program will work thread and garbage collection threads run at the same time or cross operation, thus to correct during concurrent tags, tags that changes caused by the user program continue to operate the part of object mark record, this phase of the pause time usually slightly longer than the initial mark phase, but also far shorter than the concurrent mark phase
    • Concurrent Sweep: This phase removes dead objects judged by the marking phase, freeing up memory. Since there is no need to move live objects, this phase can also be concurrent with the user thread
  • Although the CMS collector USES is concurrent collector (exclusive), but in its initialization and again marking this still need to be performed in two stages STW mechanism to pause the worker thread, but the pause time is too long, so that the all the garbage collector can’t completely don’t need STW, just as much as possible to shorten the pause time
  • Because the most time-consuming concurrent marking and concurrent cleanup phases do not require pauses, the overall collection is low-pause
  • In addition, since the user threads are not interrupted during the garbage collection phase, you should also ensure that the application user threads have enough memory available during the CMS collection process. Therefore, the CMS collector does not wait until the old age is almost completely filled, as other collectors do. Instead, it starts collecting when memory usage reaches a certain threshold to ensure that applications still have enough space to run while the CMS is working. This occurs when the memory set aside during the CMS run does not meet the program’s needsConcurrent Mode FailureFailure,This is where the virtual machine will start a backup plan: temporarily enable the Serial Old collector to redo the Old garbage collection, so that the pause time is very long
  • Garbage collection algorithms of CMS collector USES a tag – clear algorithm, which means that after each time the memory recovery, due to be executed memory recycling useless objects occupied memory space is probably some of the discrete blocks of memory, inevitably will produce some pieces of memory, so a CMS at the time of the new object allocate memory space, You will not be able to use the Bump the Pointer technique and will only be able to select the Free List to perform memory allocation
  • The mark-compression algorithm is not applicable because when concurrent cleanup occurs, the memory used by the original user thread becomes unavailable with Compact. The user thread must continue to execute if its running resources are not affected. The Mark Compact is better suited for STW scenarios

CMS advantages

  • Concurrent collection
  • Low latency

CMS disadvantages

  • Memory fragmentation occurs, resulting in insufficient space for user threads after concurrent cleanup. In the case that large objects cannot be allocated, the Full GC has to be triggered early
  • The CMS collector is very CPU sensitive, and while it does not cause user pauses during the concurrent phase, it can slow down the application and reduce overall throughput by taking up a portion of the threads
  • The CMS collector cannot handle floating garbageMay come upConcurrent Mode FailureFailure results in another Full GC. In the concurrent marking stage, the worker thread and garbage collection thread of the program run at the same time or cross, so if new garbage objects are generated in the concurrent marking stage, CMS will not be able to mark these garbage objects, which will eventually lead to the timely collection of these newly generated garbage objects. These previously unreclaimed memory Spaces can only be freed on the next GC

Parameter Settings

  • -XX:+UseConcMarkSweepGC: Manually specify the use of the CMS collector to perform memory reclamation tasks; If this parameter is enabled, it is automatically enabled-XX:+UseParNewGCOpen, that is, ParNew (Young zone) + CMS(Old zone) + Serial Old (backup) combination
  • -XX:+CMSInitiatingOccupanyFraction: Sets a threshold for heap memory usage. Once this threshold is reached, reclamation begins
    • The default value is for JDK5 and earlier versions68That is, a CMS collection is performed when the space usage of the old age reaches 68%. The default value is JDK6 or later92
    • If the memory growth is slow, you can set a slightly larger threshold. A larger threshold can effectively reduce the triggering frequency of CMS, and reduce the number of reclaiming in the old age, which can significantly improve the application performance. Conversely, if your application’s memory usage is growing rapidly, you should lower this threshold to avoid triggering the Serial Old collector frequently. Therefore, this option can effectively reduce the number of Full GC executions
  • -XX:+UseCMSCompactAtFullCollection: specifies that the memory space should be compressed after Full GC to avoid memory fragmentation. The problem, however, is that the pause times become longer because the memory compacting process cannot be executed concurrently
  • -XX:+CMSFullGCsBeforeCompaction: Sets the number of Full GC runs before the memory space is compressed
  • -XX:ParallelCMSThreads: Sets the number of CMS threads
    • The default number of threads started by CMS is(ParallelGCThreads + 3) / 4ParallelGCThreads is the number of threads for the young generation of parallel collectors. When CPU resources are tight, application performance can be very poor during the garbage collection phase due to the impact of CMS collector threads

New features in the JDK version

  • New JDK9 features: CMS has been marked as Deprecate (JEP291); If the CMS collector is enabled with the -xx :+UseConcMarkSweepGC parameter in the HotSpot VIRTUAL machine of JDK9 or later, the user will receive a warning that the CMS will be deprecated in the future
  • New features for JDK14: remove CMS garbage collector (JEP363); If you use -xx :+UseConcMarkSweepGC in JDK14, the JVM will not give an error, just a warning message, but no exit, and the JVM will automatically fall back to starting the JVM in the default GC mode (using G1).
    OpenJDK 64-Bit Server VM warning: Ignore option UseConcMarkSweepGC; support was removed in 14.0 and the VM will continue execution using the default collector.
    Copy the code

Conclusion:

  • If you want to minimize memory and parallel overhead, use Serial GC
  • If you want to maximize the throughput of your application, use Parallel GC
  • If you want to minimize the interruption or pause time of GC, use CMS GC

4.5 G1 collector: Regionalization and generation

Why release Garbage First (G1) GCS when we already have the First few powerful GCS?

  • The reason is that the business corresponding to the application is becoming larger and more complex, with more and more users. Without GC, the normal operation of the application can not be guaranteed, and often the GC of STW can not keep up with the actual demand, so it will constantly try to optimize GC. The G1 garbage collector is a new garbage collector introduced after Java7 Update 4 and is one of the most advanced developments in collector technology today
  • At the same time, to accommodate today’s expanding memory and increasing number of processors, further reduce pause times while maintaining good throughput
  • The official goal for G1 is to achieve the highest throughput possible with manageable latency, thus fulfilling the burden and expectation of a “fully functional collector.

Why is it called Garbage First (G1)?

  • Because G1 is a parallel collector, it divides heap memory into a number of unrelated regions (physically discontinuous). Use different regions to represent Eden, Survivor0, Survivor1, old age, and so on
  • The G1 GC systematically avoids area-wide garbage collection across the entire heap. G1 tracks the value of garbage accumulation in each Region (the amount of garbage collection space obtained and the experience value of garbage collection time), maintains a priority list in the background, and collects garbage from the Region with the highest value according to the allowed collection time
  • Since this approach focuses on regions where the most Garbage is collected, we gave G1 the name Garbage First.

G1 advantage

Compared to other GC collectors, G1 uses an entirely new partitioning algorithm with the following features:

  • Parallelism and concurrency
    • Parallelism: G1 can have multiple GC threads working at the same time during collection, effectively leveraging multi-core computing power. At this point the user thread is STW
    • Concurrency: G1 has the ability to alternate execution with the application, so that some work can be performed simultaneously with the application, so that the application generally does not completely block during the entire backward phase
  • Generational collection
    • From the perspective of generation, G1 is still a generational garbage collector. It can be divided into young generation and old generation. The young generation still has Eden area and Survivor area. However, from the structure of the heap, it does not require the whole Eden area, the young generation or the old generation to be continuous, nor does it insist on fixed size and fixed quantity
    • The heap space is divided into regions that contain logical young and old generations
    • Unlike previous recyclers, it takes care of both young and old generations; Compare other recyclers, either working in the younger generation or working in the older generation
  • Spatial integration
    • CMS: mark-clear algorithm, memory fragmentation, defragmentation after several GC
    • G1 divides memory into regions. Memory reclamation is based on regions. Between regions is a copy algorithm, but overall can actually be regarded as a mark-compression algorithm, both algorithms can avoid memory fragmentation. This feature helps programs run for a long time and allocate large objects without triggering the next GC prematurely because contiguity memory space cannot be found. This is especially true when the Java heap is very large
  • Predictable pause time model(i.e. Soft real-time Soft real-time)
    • This is another big advantage G1 has over CMS. In addition to pursuing low pauses, G1 models predictable pause times, allowing users to explicitly specify that no more than N milliseconds should be spent on garbage collection within a time segment of M milliseconds in length
    • Due to partitioning, G1 can select only part of the region for memory reclamation, which reduces the scope of reclamation, so that the occurrence of global pause can be well controlled
    • G1 to track the value of the accumulation of junk inside each Region size (size for recycling as well as the time needed for recycling experience), maintain a list of priority in the background, according to allow collection time, every time priority recovery value of the largest Region, ensure the G1 collector can in a limited time to get as high collection efficiency
    • G1 is not necessarily as good at delaying pauses as CMS GC is at best, but much better at worst

G1 shortcomings

  • Compared to CMS, G1 does not have a comprehensive, overwhelming advantage. For example, G1 has a higher garbage collection Footprint and Overload than CMS during user program execution
  • Empirically, CMS is more likely to outperform G1 in small memory applications, while G1 is more likely to outperform G1 in large memory applications. The balance point is between 6-8GB

Parameter Settings

  • -XX:+UseG1GCManually specify the use of the G1 collector to perform memory reclamation tasks
  • -XX:G1HeapRegionSizeSet the size of each Region. The value is a power of 2, and the range is 21MBto32MBBetween, the goal is to partition approximately based on the minimum Java heap size2048The default is heap memory1/2000
  • -XX:MaxGCPauseMillisSet the desired maximum GC pause time metric (which the JVM tries to achieve, but is not guaranteed to achieve). The default is200ms
  • -XX:ParallelGCThreadThe value of the number of GC threads when setting STW, at most8
  • -XX:ConcGCThreadsSet the number of concurrent threads to be marked, and set n to ParallelGCThreadsA quarterOr so
  • -XX:InitiatingHeapOccupancyPercentSets the Java heap usage threshold that triggers concurrent GC cycles. If this value is exceeded, GC is triggered, and the default is45

Common steps for G1 collector

G1 was designed to simplify JVM performance tuning by developers in three simple steps:

  • Step 1: Start the G1 garbage collector-XX:+UseG1GC
  • Step 2: Set the maximum memory for the heap-Xms & -Xmx
  • Step 3: Set a maximum pause time-XX:MaxGCPauseMillis

There are three garbage collection modes available in G1: YoungGC, Mixed GC, and Full GC, which are triggered under different conditions

Application scenarios of G1 collector

  • Service-oriented applications, for machines with large memory and multiple processors (not surprising in a normal size heap), the most important applications require low GC latency and provide solutions for applications with large stacks. For example, when the heap size is about 6GB or larger, the predictable pause time can be less than 0.5 seconds, and G1 ensures that each GC pause is not too long by incrementally cleaning only some regions at a time rather than all of them
  • To replace the CMS collector in JDK1.5; Using G1 may be better than using CMS in the following situations:
    • More than 50% of the Java heap is occupied by active data
    • The frequency of object assignment or chronological ascension varies greatly
    • GC pauses are too long (longer than 0.5 to 1 second)
  • In addition to G1, other garbage collectors use a built-in JVM thread to perform multi-threaded GC operations. G1 GC can use application threads to do the background GC work, which is called to help speed up the garbage collection process when the JVM’s GC thread is slow

Partition Region: Divide the whole into parts

Using the G1 collector, it divides the entire Java heap into as many as 2048 independent Region blocks of the same size, each Region block size depending on the actual size of the heap, and the overall Region block size is controlled between 1MB and 32MB, and is a power of N of 2. -xx :G1HeapRegionSize Can be set. All regions are the same size and do not change during the lifetime of the JVM

Although the concept of Cenozoic and oldyn is still retained, Cenozoic and oldyn are no longer physically separated; they are collections of parts of regions (which do not need to be continuous). Dynamic Region allocation is used to achieve logical continuity

  • A Region may belong to an Eden, Survivor, or Old/Tenured memory Region. However, a Region can belong to only one role. Blank Spaces in the figure represent unused memory areas
  • The G1 garbage collector also adds a new memory region calledHumongousThe memory area, block H in the figure, is mainly used to store large objects, if exceeded1.5Region, put it in the H block
  • The reason for setting H: Large objects in the heap are allocated directly to the old age by default, but if it is a short-lived large object, the heap garbage collector is adversely affected. To solve this problem, G1 has a Humongous section, which is dedicated to large objects. If an H block does not fit a large object, G1 looks for contiguous H blocks to store. Sometimes you have to start the Full GC in order to find consecutive H regions. Most of G1’s behavior treats the H region as part of the old age

Overview of the G1 GC garbage collection process

The garbage collection process of G1 GC mainly includes the following three steps:

  • Young GC
  • Concurrent Marking in the old days
  • Mixed GC
  • (Single-threaded, exclusive, high-intensity Full GC still exists if needed, providing a fail-safe mechanism for GC appraisers, called brute force collection.)

Clockwise, young GC -> Young GC + Concurrrent mark -> Mixed GC is collected in sequence

  • The application allocates memory, and the young generation collection process begins when the young generation’s Eden range is exhausted: G1’s young generation collection phase is a parallel, exclusive collector. During the young generation collection period, the G1 GC suspends all application threads and starts multithreading to perform the young generation collection. Then move the Survivor object from the young generation to the Survivor or old generation, or possibly both
  • When heap memory usage reaches a certain value (45% by default), the old-age concurrent marking process begins
  • Mark completion to begin the mixed recycling process immediately. For a mixed payback period, the G1 GC moves live objects from the old period to the free period, which becomes part of the old period. Unlike the young generation, the G1 collector of the old generation does not need to recycle the entire old generation, but only scan and reclaim a small number of old regions at a time. At the same time, the old Region is reclaimed along with the young generation
  • For example, a Web server with a Java process with a maximum heap memory of 4 gigabytes responds to 1500 requests per minute and allocates about 2 gigabytes of new memory every 45 seconds. G1 does a young generation collection every 45 seconds, with a total heap utilization rate of 45% every 31 hours, and begins the old generation concurrent marking process, with four or five mixed collections after the marking is complete

Remembered Set

  • An object referenced by different regions: A Region cannot be independent, and objects in one Region can be referenced by objects in any other Region. Do you need to scan the entire Java heap to determine whether an object is alive? In the case of other generational collectors (more notably G1), collecting the new generation also has to scan the old generation at the same time, which reduces the efficiency of the Minor GC
  • The solution
    • Regardless of G1 or any generational collector, the JVM uses Remembered Set to avoid global scans
    • Each Region has a corresponding Remembered Set
    • Each time a Reference is written, a Write Barrier is generated to check whether the Reference to be written is in a different Region from that of the Reference. If not, the related references are recorded in the Remembered Set of the Region where the reference points to the object through CardTable.
    • Adding the Remembered Set scope to the enumeration at the root of the GC ensures that no global scans and no omissions occur when garbage collection is in progress

G1 recovery process 1: Young generation GC

When JVM starts, G1 prepares Eden area first, and the program continuously creates objects to Eden area during the running process. When Eden space runs out, G1 will start a young generation garbage collection process

Young generation garbage collection will only collect Eden and Survivor areas

In YGC, G1 stops the execution of the application (STW) first and creates a Collection Set, which refers to the Collection of memory segments that need to be reclaimed. The Collection in the young generation reclamation process contains all memory segments in the Eden area and Survivor area of the young generation

Then start the following recycling process:

  • Phase 1: Scan the roots. The root refers to the object to which the static variable points, the local variable in the chain of method calls being executed, and so on. The root reference, along with the external reference to the RSet record, serves as the entry point for scanning the living object
  • Phase 2: Update the RSet. Process cards in the Dirty Card queue (see below) and update the RSet. After this phase is complete, the RSet can accurately reflect the reference of the object in the memory segment where the old age is located
  • Stage 3: Processing rSETS. Identify the objects in Eden that are pointed to by the old objects. The objects in Eden that are pointed to are considered alive
  • Stage 4: Copy objects. At this stage, the object tree is traversed, and the surviving objects in the memory segment of Eden area will be copied to the hollow memory segment of Survivor area. If the age of surviving objects in the memory segment of Survivor area does not reach the threshold, the age will be increased by 1. When the age reaches the threshold, the object tree will be copied to the hollow memory segment of Old area. If Survivor space is insufficient, some data in Eden space will be promoted directly to the old space
  • Stage 5: Handling references. Handle Soft, Weak, Phantom, Final, JNI Weak references, etc. Finally, the data in Eden space is empty, GC stops working, and the objects in the target memory are continuously stored without fragmentation. Therefore, the replication process can achieve the effect of memory consolidation and reduce fragmentation

Dirty Card Queue: For the application’s reference assignment statement Object. field=object, the JVM performs special operations before and after to enqueue a Card that holds object references in the Dirty Card Queue. During the recycle of the young generation, G1 will process all cards in the Dirty Card Queue to update the RSet and ensure that the RSet accurately reflects the reference relationship in real time. Why not update the RSet directly at the reference assignment statement? This is for the sake of performance, RSet processing requires thread synchronization, which can be very expensive, using queue performance is much better

G1 recovery process ii: Concurrent marking process

  • Stage 1: Initial markup. Marks objects directly reachable from the root node. This phase is STW and triggers a young GC
  • Phase 2: Root Region Scanning. The G1 GC scans the old-age region objects that are directly reachable from the Survivor region and marks the referenced objects. This process must be completed before the Young GC
  • Stage 3: Concurrent Marking. Concurrent marking (and application concurrent execution) throughout the heap may be interrupted by the Young GC. During the concurrent marking phase, if all objects in a region object are found to be garbage, the region is immediately reclaimed. During concurrent marking, the object activity (the percentage of live objects in the region) is calculated for each region
  • Stage 4: Remark. As the application continues, we need to fix the last mark result, which is for STW. G1 uses a faster initial snapshot algorithm than CMS: Snapshot-at-the-beginning (SATB)
  • Stage 5: Cleanup. Calculate the inventory objects and GC recovery ratio of each region, sort them, and identify areas that can be mixed recovery, paving the way for the next stage. Is the STW
  • Stage 6: Concurrent cleanup. Identify and clean areas that are completely free

G1 Recovery process 3: Mixed recovery

When more and more objects are promoted to Old regions, in order to avoid running out of heap memory, the virtual machine will trigger a Mixed garbage collector, namely Mixed GC. This algorithm is not an Old GC, but will reclaim the whole Young Region as well as part of the Old Region. Note here: part of the old era, not all of it. You can select which Old regions to collect, thus controlling the garbage collection time. Also note that the Mixed GC is not the Full GC

  • After the concurrent marking ends, the segments that are 100% garbage in the old age are reclaimed and the segments that are partially garbage are calculated. By default, these older memory segments are split8Times (can pass-XX:G1MixedGCCountTargetSet) is reclaimed
  • The Collection Set of a mixed Collection consists of one-eighth of old age segments, Eden segment, and Survivor segment. The algorithm of hybrid collection is exactly the same as the algorithm of young generation collection, but it collects more memory segments of the old generation. Refer to the recycling process of the young generation above for details
  • Since memory segments are recycled eight times by default in older generations, G1 prioritises memory segments with more garbage. The higher the percentage of garbage in memory segments, the more garbage will be collected first. There is a threshold that determines whether memory segments are reclaimed-XX:G1MixedGCLiveThresholdPercentBy default,65%, which means that garbage has to make up 65% of the memory segment before it is collected. If the garbage ratio is too low, it means that there is a high percentage of live objects, which will take more time to replicate
  • Mixed collection does not have to be done 8 times, there is a threshold-XX:G1HeapWastePercent, the default value is10%, meaning that 10% of the total heap memory is allowed to be wasted, meaning that if the percentage of garbage that can be collected is less than 10% of the heap memory, no mixed collection is performed. Because GC takes a lot of time but recycles very little memory

G1 Collection optional process 4: Full GC

The G1 was designed to avoid Full GC. But if that doesn’t work, G1 stops application execution (STW) and uses a single-threaded memory reclamation algorithm for garbage collection, with poor performance and long application pause times

To avoid Full GC, you need to adjust once it happens. When will Full GC happen? For example, if the heap is too small, G1 will fall back to Full GC when there is no empty segment available for copying live objects, a situation that can be resolved by increasing memory

There are two possible reasons for G1 Full GC:

  • Evacuation time does not have sufficient to-space to hold the object of promotion
  • Space runs out before the concurrent processing completes

G1 recovery process: Supplement

According to Official information from Oracle, the Evacuation phase was designed to be executed concurrently with user programs, but this is complicated and is not urgent, considering that G1 only reclaims part of a Region and pause times are controlled by the user. Instead, we chose to put this feature into the low-latency garbage collector (ZGC) that emerged after G1. In addition, considering that G1 is not only geared toward low latency, pausing the user thread can maximize garbage collection efficiency, so the implementation of pausing the user thread completely was chosen to ensure throughput

G1 collector optimization suggestions

  • Young generation size
    • Avoid the use of-Xmnor-XX:NewRatioAnd other related options explicitly set the young generation size
    • Fixed the size of the young generation to override the pause time target
  • Don’t be too harsh on time-outs
    • The throughput goal for the G1 GC is 90% application time and 10% garbage collection time
    • When evaluating G1 GC throughput, don’t be too harsh with pause time goals. Being too demanding means that you are willing to incur more garbage collection overhead, which directly affects throughput

4.6. Summary of garbage collector

As of JDK8, there are seven different garbage collectors. Each different garbage collector has different characteristics. In the specific use, you need to choose different garbage collectors according to the specific situation

GC development stage:

- Serial -> Parallel -> CMS -> G1 -> ZGCCopy the code
Garbage collector classification Role position Using the algorithm The characteristics of Applicable scenario
Serial Serial operation The new generation Replication algorithm Speed of response priority This mode applies to the Client mode in a single-CPU environment
ParNew Run in parallel The new generation Replication algorithm Speed of response priority It works with the CMS in Server mode with multiple cpus
Parallel Run in parallel The new generation Replication algorithm Throughput priority Ideal for scenarios where background computing does not require much interaction
Serial Old Serial operation The old s Mark-compression algorithm Speed of response priority This mode applies to the Client mode in a single-CPU environment
Parallel Old Run in parallel The old s Mark-compression algorithm Throughput priority Ideal for scenarios where background computing does not require much interaction
CMS Run concurrently The old s Mark-clear algorithm Speed of response priority Applicable to Internet or B/S services
G1 Concurrent and parallel running New generation, old age Mark-compression, copy algorithm Speed of response priority Service-oriented applications

How do I choose a garbage collector

Garbage collector configuration is an important choice for JVM optimization, and choosing the right garbage collector can make a big difference in JVM performance

  • Prioritizing the heap size allows the JVM to adapt
  • If memory is less than 100M, use a serial collector
  • If it is a single-core, stand-alone program, and there is no pause time requirement, use a serial collector
  • If it is multi-CPU, requires high throughput, and allows pause times of more than 1 second, choose parallelism or the JVM’s choice
  • If you have multiple cpus and are looking for low pause times that require fast responses (such as a delay of no more than 1 second, as in Internet applications), use a concurrent collector. The G1 is officially recommended for high performance. The current Internet projects are basically using G1

5. GC log analysis

List of parameters for memory allocation and garbage collection

  • -XX:+PrintGC: Outputs GC logs. Similar to:-verbose:gc
  • -XX:+PrintGCDetails: Displays detailed GC logs
  • -XX:+PrintGCTimeStamps: Outputs the GC timestamp (in base time)
  • -XX:+PrintGCDateStamps: Outputs the GC timestamp (in date form, for example, 2013-05-04T21:53:59.234+0800)
  • -XX:+PrintHeapAtGC: Prints heap information before and after GC
  • -Xloggc:.. /logs/gc.log: Indicates the output path of the log file

For the following code:

import java.util.ArrayList;

/** * -Xms60m -Xmx60m */
public class GCLOGTEST {
    public static void main(String[] args) {
        ArrayList<byte[]> list = new ArrayList<>();

        for (int i = 0; i < 500; i++) {
            byte[] arr = new byte[1024 * 100]; // 100KBlist.add(arr); }}}Copy the code

In the JDK8 environment, the output of various parameters is as follows:

– XX: + PrintGC or verbose: gc

[GC (Allocation Failure)  15331K->14766K(58880K), 0.0084983 secs]
[GC (Allocation Failure)  30094K->29936K(58880K), 0.0099874 secs]
[Full GC (Ergonomics)  29936K->29833K(58880K), 0.0083459 secs]
[Full GC (Ergonomics)  45179K->44735K(58880K), 0.0080480 secs]
Copy the code
  • Parameter analysis:
    • GC, Full GC: the type of GC; GC is only performed on Cenozoic, Full GC includes permanent generation, Cenozoic and old age. If Full, the GC has STW
    • Allocation Failure: The reason why GC occurs
    • 15331K->14766K: size of heap before GC and size after GC
    • 58880K: Current heap size
    • 0.0084983 SECS: GC duration

-XX:+PrintGCDetails

[GC (Allocation Failure) [PSYoungGen: 15331K->2528K(17920K)] 15331K->14854K(58880K), 0.0147277 secs] [Times: user=0.01 sys=0.03, real=0.02 secs] 
[GC (Allocation Failure) [PSYoungGen: 17856K->2532K(17920K)] 30182K->30040K(58880K), 0.0152757 secs] [Times: user=0.01 sys=0.03, real=0.02 secs] 
[Full GC (Ergonomics) [PSYoungGen: 2532K->0K(17920K)] [ParOldGen: 27508K->29833K(40960K)] 30040K->29833K(58880K), [Metaspace: 2653K->2653K(1056768K)], 0.0138732 secs] [Times: user=0.03 sys=0.00, real=0.01 secs] 
[Full GC (Ergonomics) [PSYoungGen: 15346K->4000K(17920K)] [ParOldGen: 29833K->40735K(40960K)] 45179K->44735K(58880K), [Metaspace: 2653K->2653K(1056768K)], 0.0156897 secs] [Times: user=0.02 sys=0.02, real=0.02 secs] 
Heap
 PSYoungGen      total 17920K, used 9854K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 64% used [0x00000007bec00000.0x00000007bf59f870.0x00000007bfb00000)
  from space 2560K, 0% used [0x00000007bfd80000.0x00000007bfd80000.0x00000007c0000000)
  to   space 2560K, 0% used [0x00000007bfb00000.0x00000007bfb00000.0x00000007bfd80000)
 ParOldGen       total 40960K, used 40735K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 99% used [0x00000007bc400000.0x00000007bebc7c98.0x00000007bec00000)
 Metaspace       used 2659K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
Copy the code
  • Parameter analysis:
    • PSYoungGenThe application of the Parallel Insane garbage collector
      • Using Serial the collector in the New Generation name is Default New Generation, so the display is"[DefNew"
      • Using the ParNew collector in the new generation will become the name[ParNewParallel New Generation
      • Using the G1 collector, it displays asgarbage-first heap
    • ParOldGen: Changes in size before and after GC using a Parallel Old garbage collector
    • Metaspace: changes in the size of metadata areas before and after GC. Metadata areas were introduced in JDK1.8 to replace permanent generations
    • XXX secs: refers to the time taken by GC
    • Times: user=0.03 sys=0.00, real= 0.01secs: Sys refers to the time spent waiting for system calls or system events. Real refers to the time from the start to the end of the GC, including the actual time that other processes occupy the time slice, which is the actual elapsed time. Due to multicore, the sum of times may exceed real time

-XX:+PrintGCTimeStamps

0.239: [GC (Allocation Failure) [PSYoungGen: 15331K->2532K(17920K)] 15331K->14782K(58880K), 0.0141273 secs] [Times: user=0.02 sys=0.05, real=0.02 secs] 
0.257: [GC (Allocation Failure) [PSYoungGen: 17860K->2532K(17920K)] 30110K->29984K(58880K), 0.0174320 secs] [Times: user=0.02 sys=0.05, real=0.02 secs] 
0.274: [Full GC (Ergonomics) [PSYoungGen: 2532K->0K(17920K)] [ParOldGen: 27452K->29833K(40960K)] 29984K->29833K(58880K), [Metaspace: 2653K->2653K(1056768K)], 0.0151363 secs] [Times: user=0.02 sys=0.01, real=0.01 secs] 
0.293: [Full GC (Ergonomics) [PSYoungGen: 15346K->4000K(17920K)] [ParOldGen: 29833K->40735K(40960K)] 45179K->44735K(58880K), [Metaspace: 2653K->2653K(1056768K)], 0.0146960 secs] [Times: user=0.03 sys=0.04, real=0.01 secs] 
Heap
 PSYoungGen      total 17920K, used 9854K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 64% used [0x00000007bec00000.0x00000007bf59f870.0x00000007bfb00000)
  from space 2560K, 0% used [0x00000007bfd80000.0x00000007bfd80000.0x00000007c0000000)
  to   space 2560K, 0% used [0x00000007bfb00000.0x00000007bfb00000.0x00000007bfd80000)
 ParOldGen       total 40960K, used 40735K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 99% used [0x00000007bc400000.0x00000007bebc7c98.0x00000007bec00000)
 Metaspace       used 2659K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
Copy the code

The number 0.239 before GC information indicates the running time of the virtual machine after startup

-XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps

2021-07-03T10:53:34.686- 0800:0.143: [GC (Allocation Failure) [PSYoungGen: 15331K->2512K(17920K)] 15331K->14822K(58880K), 0.0096187 secs] [Times: user=0.01 sys=0.02, real=0.01 secs] 
2021-07-03T10:53:34.698- 0800:0.155: [GC (Allocation Failure) [PSYoungGen: 17840K->2532K(17920K)] 30150K->30000K(58880K), 0.0108485 secs] [Times: user=0.02 sys=0.04, real=0.01 secs] 
2021-07-03T10:53:34.709- 0800:0.166: [Full GC (Ergonomics) [PSYoungGen: 2532K->0K(17920K)] [ParOldGen: 27468K->29833K(40960K)] 30000K->29833K(58880K), [Metaspace: 2653K->2653K(1056768K)], 0.0087918 secs] [Times: user=0.02 sys=0.00, real=0.01 secs] 
2021-07-03T10:53:34.719- 0800:0.176: [Full GC (Ergonomics) [PSYoungGen: 15346K->4000K(17920K)] [ParOldGen: 29833K->40735K(40960K)] 45179K->44735K(58880K), [Metaspace: 2653K->2653K(1056768K)], 0.0092554 secs] [Times: user=0.02 sys=0.02, real=0.01 secs] 
Heap
 PSYoungGen      total 17920K, used 9854K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 64% used [0x00000007bec00000.0x00000007bf59f870.0x00000007bfb00000)
  from space 2560K, 0% used [0x00000007bfd80000.0x00000007bfd80000.0x00000007c0000000)
  to   space 2560K, 0% used [0x00000007bfb00000.0x00000007bfb00000.0x00000007bfd80000)
 ParOldGen       total 40960K, used 40735K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 99% used [0x00000007bc400000.0x00000007bebc7c98.0x00000007bec00000)
 Metaspace       used 2659K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
Copy the code

2021-07-03T10:53:34.686-0800 You can see that specific Date information is added before GC information

-XX:+PrintHeapAtGC

{Heap before GC invocations=1 (full 0):
 PSYoungGen      total 17920K, used 15331K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 99% used [0x00000007bec00000.0x00000007bfaf8f70.0x00000007bfb00000)
  from space 2560K, 0% used [0x00000007bfd80000.0x00000007bfd80000.0x00000007c0000000)
  to   space 2560K, 0% used [0x00000007bfb00000.0x00000007bfb00000.0x00000007bfd80000)
 ParOldGen       total 40960K, used 0K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 0% used [0x00000007bc400000.0x00000007bc400000.0x00000007bec00000)
 Metaspace       used 2652K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
Heap after GC invocations=1 (full 0):
 PSYoungGen      total 17920K, used 2532K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 0% used [0x00000007bec00000.0x00000007bec00000.0x00000007bfb00000)
  from space 2560K, 98% used [0x00000007bfb00000.0x00000007bfd79160.0x00000007bfd80000)
  to   space 2560K, 0% used [0x00000007bfd80000.0x00000007bfd80000.0x00000007c0000000)
 ParOldGen       total 40960K, used 12289K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 30% used [0x00000007bc400000.0x00000007bd0007a0.0x00000007bec00000)
 Metaspace       used 2652K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
}
{Heap before GC invocations=2 (full 0):
 PSYoungGen      total 17920K, used 17860K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 99% used [0x00000007bec00000.0x00000007bfaf7ff0.0x00000007bfb00000)
  from space 2560K, 98% used [0x00000007bfb00000.0x00000007bfd79160.0x00000007bfd80000)
  to   space 2560K, 0% used [0x00000007bfd80000.0x00000007bfd80000.0x00000007c0000000)
 ParOldGen       total 40960K, used 12289K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 30% used [0x00000007bc400000.0x00000007bd0007a0.0x00000007bec00000)
 Metaspace       used 2653K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
Heap after GC invocations=2 (full 0):
 PSYoungGen      total 17920K, used 2516K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 0% used [0x00000007bec00000.0x00000007bec00000.0x00000007bfb00000)
  from space 2560K, 98% used [0x00000007bfd80000.0x00000007bfff5150.0x00000007c0000000)
  to   space 2560K, 0% used [0x00000007bfb00000.0x00000007bfb00000.0x00000007bfd80000)
 ParOldGen       total 40960K, used 27492K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 67% used [0x00000007bc400000.0x00000007bded9120.0x00000007bec00000)
 Metaspace       used 2653K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
}
{Heap before GC invocations=3 (full 1):
 PSYoungGen      total 17920K, used 2516K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 0% used [0x00000007bec00000.0x00000007bec00000.0x00000007bfb00000)
  from space 2560K, 98% used [0x00000007bfd80000.0x00000007bfff5150.0x00000007c0000000)
  to   space 2560K, 0% used [0x00000007bfb00000.0x00000007bfb00000.0x00000007bfd80000)
 ParOldGen       total 40960K, used 27492K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 67% used [0x00000007bc400000.0x00000007bded9120.0x00000007bec00000)
 Metaspace       used 2653K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
Heap after GC invocations=3 (full 1):
 PSYoungGen      total 17920K, used 0K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 0% used [0x00000007bec00000.0x00000007bec00000.0x00000007bfb00000)
  from space 2560K, 0% used [0x00000007bfd80000.0x00000007bfd80000.0x00000007c0000000)
  to   space 2560K, 0% used [0x00000007bfb00000.0x00000007bfb00000.0x00000007bfd80000)
 ParOldGen       total 40960K, used 29833K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 72% used [0x00000007bc400000.0x00000007be122480.0x00000007bec00000)
 Metaspace       used 2653K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
}
{Heap before GC invocations=4 (full 2):
 PSYoungGen      total 17920K, used 15346K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 99% used [0x00000007bec00000.0x00000007bfafcb50.0x00000007bfb00000)
  from space 2560K, 0% used [0x00000007bfd80000.0x00000007bfd80000.0x00000007c0000000)
  to   space 2560K, 0% used [0x00000007bfb00000.0x00000007bfb00000.0x00000007bfd80000)
 ParOldGen       total 40960K, used 29833K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 72% used [0x00000007bc400000.0x00000007be122480.0x00000007bec00000)
 Metaspace       used 2653K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
Heap after GC invocations=4 (full 2):
 PSYoungGen      total 17920K, used 4000K [0x00000007bec00000.0x00000007c0000000.0x00000007c0000000)
  eden space 15360K, 26% used [0x00000007bec00000.0x00000007befe8280.0x00000007bfb00000)
  from space 2560K, 0% used [0x00000007bfd80000.0x00000007bfd80000.0x00000007c0000000)
  to   space 2560K, 0% used [0x00000007bfb00000.0x00000007bfb00000.0x00000007bfd80000)
 ParOldGen       total 40960K, used 40735K [0x00000007bc400000.0x00000007bec00000.0x00000007bec00000)
  object space 40960K, 99% used [0x00000007bc400000.0x00000007bebc7c98.0x00000007bec00000)
 Metaspace       used 2653K, capacity 4486K, committed 4864K, reserved 1056768K
  class space    used 287K.capacity 386K.committed 512K.reserved 1048576K
}
Copy the code

-Xloggc:gc.log

GC log analysis tool

Common log analysis tools include GCViewer, GCEasy, GCHisto, GCLogViewer, Hpjmeter, Garbagecat, etc.

GCEasy gceasy. IO/is recommended

6. New developments in garbage collectors

GC is still evolving rapidly, and the current default option, G1 GC, is constantly being improved. Many of the previously perceived shortcomings, such as serial Full GC, inefficiency of Card Table scanning, have been greatly improved. For example, since JDK10, Full GC has been run in Parallel and in many scenarios performs slightly better than the Parallel Full GC implementation of Parallel GC

Even the Serial GC, while ancient, is not necessarily obsolete in its simple design and implementation. Its own overhead, both for GC-related data structures and threads, is minimal, so with the rise of cloud computing, Serial GC has found a new arena in new application scenarios such as Serverless

Not so good is the CMS GC, which has been marked deprecated in JDK9 and removed in JDK14 due to theoretical flaws in its algorithm, although it has a very large user base now

JDK11 new features

  • JEP318 Epsilon: A No – Op Garbage Collector (memory allocation, only do not do memory recovery) openjdk.java.net/jeps/318
  • JEP333 ZGC: A Scalable Low Latency Garbage Collector (Scalable low-latency Garbage Collector, at the experimental stage) openjdk.java.net/jeps/333

Shenandoah GC is new in Open JDK12

  • Advantages: Low pause times; Disadvantages: Throughput degradation under high operating load
  • Shenandoah is undoubtedly the loneliest of the many GC’s. It was the first HotSpot garbage collector not developed by the Oracle team, which was inevitably officially sidelined. For example, Oracle, which claims no difference between OpenJDK and OracleJDK, still refuses to support Shenandoah in OracleJDK12 (currently only available in OpenJDK).
  • Shenandoah garbage Collector was originally an implementation of a garbage collector research project, Pauseless GC, conducted by RedHat to address the need for low pauses for memory reclamation on JVMS and contributed to the OpenJDK in 2014
  • According to the Shenandoah team, the garbage collector pauses regardless of the size of the heap, meaning 99.9% of the time can limit garbage collection pauses to 10ms regardless of whether the heap is set to 200MB or 200GB. However, actual usage performance will depend on the actual working heap size and workload

New in JDK14: ZGC

Reference docs.oracle.com/en/java/jav…

  • ZGC is highly similar to Shenandoah’s goal of achieving low latency that limits gc pauses to 10ms for any heap size with as little impact on throughput as possible
  • The ZGC collector is defined in Understanding the Java Virtual Machine as a garbage collector based on Region memory layout, with (for now) no generation, using techniques such as read barriers, dye Pointers, and memory multiple mapping to implement concurrent mark-compression algorithms, and with low latency as the primary goal
  • The working process of ZGC can be divided into four stages:
    • Concurrent tags
    • Concurrent preparatory reallocation
    • Concurrent redistribution
    • Concurrent remapping
  • ZGC is executed almost everywhere concurrently except for the STW that is initially marked, so the pause time is almost spent on the initial tag, which is very little real time
  • Prior to JDK14, ZGC was supported only by Linux
    • Application on MacOS openjdk.java.net/jeps/364 JEP364: ZGC
    • On the Windows openjdk.java.net/jeps/365 JEP365: ZGC applications
  • Parameters:-XX:+UnlockExperimentalVMOptions -XX:+UseZGC

Other garbage collectors

  • AliGC: A garbage collector developed by Alibaba JVM team based on G1 algorithm for LargeHeap application scenarios
  • Zing GC: low latency GC www.infoq.com/articles/az…