preface

In general, the signs of progress in Java development are as follows :1. 2. Good coding style; Familiar with automatic test technology; Familiar with the process and method of code refactoring; 5. Master multi-threaded development; 6. In-depth understanding of virtual machine (JVM) mechanisms. That’s why I ask these questions when I interview senior developers. There has been an article on multithreaded development, and due to space constraints, this article will focus on the Java Garbage Collection (GC) mechanism. Habits asking why is a good habit, and here’s why you should know about garbage collection.

why

Some people might think that Java has an automatic garbage collection mechanism, but depending on the JVM, we don’t have to worry about that. It’s true that Java GC has been around for a long time and is getting better and more reliable in most cases. However, in actual work, OOM or Stop The World problems caused by garbage can’t be collected automatically in time may still occur, and such problems are generally difficult to locate and solve. Understanding the Java garbage collection mechanism can help us better prevent and solve various OOM problems. We all know that Java automatically allocates memory dynamically and automatically cleans up memory for us, so I don’t have to worry about running out of memory anymore. Well, that’s a later story. First we need to remember what kind of data or memory is automatically reclaimed by the JVM, and is it possible that the JVM cleans up the things we need to use? Further, what would we do if we implemented GC? Let’s start with what kind of data is automatically cleaned.

The basis of memory reclamation

In the mainstream implementation of the mainstream commercial language, whether memory can be reclaimed is determined by reachable analysis to determine whether the object is alive. The basic idea of the algorithm is to search down from a series of “GC Roots” objects as starting points. The path searched is called reference link. When an object is not connected to GC Roots by any reference chain, it is proved that the object is not available. Note that GC Roots are not the only node and can include several sources: – Object referenced in the VM stack – Object referenced in the static attribute – Object referenced in the method area – Object referenced in the constant – Object referenced in the local method The above references can be classified into strong references, soft references, weak references, and virtual references. Strong references are the most common, Object obj=new Object() is a strong reference, and the referenced Object will not be collected as long as garbage collectors exist. Soft references describe objects that are useful but not necessary. For objects associated with soft references, the system will list these objects into the scope of the collection for a second collection before OOM occurs. Weak references are also nonessential for describing objects, but they are even weaker, and objects associated with weak references only survive until the next GC. Virtual references are also called ghost references or phantom references. Whether or not an object has a virtual reference does not affect whether or not it is reclaimed, except that if it has a virtual reference it receives a notification from the system when it is reclaimed.

There is another problem, even if an object is determined to be “unreachable”, it may not be recycled. The special case is that if the object has Finalize () method, and the link between Finalize () method and GC Roots is re-established, then the object can survive, but it should be noted that The Finalize () method can only be run once. If they are judged twice, they will die.

As you can see from the criteria above, the JVM is extremely cautious and almost impossible to clean up useful data.

GC algorithm

Mark-clear algorithm

The algorithm is the simplest algorithm, which is mainly divided into two stages: 1. Mark the object to be recycled; 2. 2. Recycle all marked objects after marking is complete. The specific process is shown in the figure below (reproduced from the Internet).

However, it has two shortcomings: 1. Efficiency problem: the two processes of mark and clear are not efficient, and need to go through twice. 2. Space problem: a large number of discontinuous memory fragments will be generated after the mark is clear. If there are too many memory fragments, large objects cannot be stored later.

Replication algorithm

The algorithm divides the memory into two equal pieces, uses only one piece at a time, and when the piece is used up, copies the remaining objects onto the other piece and clears the used memory all at once. In this way, half of the memory is reclaimed each time, and there is no memory fragmentation problem. On the implementation level, as long as the heap top pointer, in order to allocate memory, simple implementation, efficient operation, but only half of the memory can be used.



This algorithm uses a different approach to solve all the mark-sweep problems at once, but at the expense of a lot of memory, which is a big sacrifice, but it’s a brave attempt to sell an important step forward for GC algorithms.

Later, there will be GC implementation corresponding to this algorithm, in fact, it does not need to sacrifice half of the memory, according to the distribution of the life cycle of the object in memory can reduce the memory sacrifice, which is the later generation, old generation and survivor space origin.

Mark-collation algorithm

Because the efficiency of the replication algorithm is very low when the object survival rate is high, some people put forward the mark-collation algorithm to solve this kind of memory reclamation problem.

This algorithm is the same as the original mark-clean marking phase, but the difference is that in the clear phase, instead of cleaning up the recyclable objects directly, all surviving objects are moved to one end and the unexpected memory is cleaned up directly at the end boundary.

Generational algorithm

Current GC implementations of commercial JVMS use generational algorithms, which divide memory into several regions and each region is treated with a different collection algorithm. Partitioning criteria are based on the lifetime of objects. Copy algorithms are used for areas with short life cycles, and mark-tidy or mark-clean algorithms are used for areas with long life cycles. Knowing the above GC algorithms, you can ask which GC algorithm you want to hear when someone asks you to explain the garbage collection mechanism.

The basics of GC implementation

Partition of memory

To improve the efficiency of garbage collection, memory is divided according to the life cycle of objects. The storage area with a long life cycle is called the old era, and the storage area with a short life cycle is called the new generation.

The origin of Stop the World

Stop the world refers to the phenomenon of having to pause all Java threads of execution while GC is being performed. In order to achieve accurate GC, you don’t want to have a one in a million chance that your data will be cleared by GC by mistake. You need to perform global reachablility analysis, i.e. enumerating GC Roots. Although countless man-months have been devoted to eliminating Stop the World, so far all GCS have required a certain amount of time to complete the enumeration of GC Roots, but with proper setup and development, this time can be reduced to an acceptable or even undetectable level.

Memory allocation policy

As mentioned above, the time of life of objects in memory is not consistent. Studies show that most objects are “born and die overnight”, so there is no need for a 1:1 allocation of old and new generation. Divide the memory into a large old Survivor and two smaller new Eden, using Eden and Survivor each time. When the collection is made, Eden and Survivor surviving objects are copied to another Survivor once, and Eden and used Survivor space are cleaned up after the collection. By default, Eden and Survivor are 8:1. According to the previous statistics, in general, the space is sufficient, but in special cases, Survivor space is insufficient to save the existing data, so Eden needs to make some space to save the object. This strategy is to trade space for time. If there is insufficient space, then trade time for space. This strategy is very effective in general, but may lead to performance bottlenecks in poor memory configuration or special business scenarios. Memory allocation strategy:

  • Objects are preferentially allocated in Eden of the new generation
  • Big object goes straight to the old age
  • Long-lived objects enter the old age

The realization of the GC

Serial collector

One of the earliest implementations of GC, the single-threaded GC collector, caused other threads to Stop during GC, causing Stop the world to occur. The DEFAULT generation collector that the JVM runs in Client mode is simple and efficient, with a perfectly acceptable pause time when collection space is limited. There is no best, only the most appropriate.

ParNew collector

ParNew is essentially a multithreaded version of Serial, the default new generation collector for Sever mode, which can be used in conjunction with the CMS collector for more powerful combinations. ParNew, a multithreaded version, performs better on multiple cpus than Serial.

Parallel avenge

The new generation collector, using the replication algorithm. The collector features the ability to adjust pause times by controlling throughput (that is, the ability to adjust the amount of time taken up by garbage collection), that is, by reducing throughput to reduce the amount of time each garbage collection is paused. From this collector you can see that everything is specific to the application scenario, there is no optimal only the most suitable.

Serial Old collector

Is an older version of Serial, a single-threaded, mark-collation algorithm. It is mainly matched with the Paralled Scavenge or as a backup to the CMS collector.

Parallel Old collector

The Parallel Old avenge is an older version of the Parallel Insane, a combination of the Parallel Insane and Parallel Old can be preferred for the insane and CPU-sensitive applications.

CMS collector

The Concurrent Mark Sweep (CMS) collector is a collector that aims to obtain the shortest collection pause, and is suitable for application scenarios that emphasize service response speed. Basically, it gives mark-algorithm, but the specific process can be divided into:

  1. Initial tags, which mark objects directly associated with GC Roots, are very fast, but may exist to Stop the world
  2. Concurrent markup, which takes longer but can work with user threads
  3. Re-mark to correct objects that may have changed reachability due to concurrent marking, and there may be Stop the world
  4. Concurrent cleanup, which takes longer, but can work with user threads

The collector is cleverly designed to minimize pause times and to synchronize time-consuming work with the user thread.

But CMS also has certain problems

  1. Very sensitive to CPU resources
  2. Unable to handle floating garbage
  3. Large amounts of debris may be generated

G1 collector

The G1 (Garbage First) collector is currently the most cutting-edge and service-oriented collector. Features are as follows: Parallel and concurrent: Using multiple cpus to reduce Stop the world time generation collection: Different ways to manage old and new spatial integration: mark-collation thinking, no fragmentation problems predictable pauses: There is a model that predicts the pause time and it goes through the same process as a CMS: initial tag, concurrent tag, final tag, filter recycle.

Main References

1. In-depth Understanding of the Java Virtual Machine