Yige

Yige

Build

JVM Series - Garbage Collection in JVM

JVM Series - Garbage Collection in JVM#

Content from:

  1. Garbage Collection Strategies and Algorithms
  2. HotSpot Garbage Collector

1. Garbage Collection Determination#

Types of References#

  • Strong Reference: As long as a strong reference exists, the garbage collector will never reclaim the referenced object.

  • Soft Reference: When the JVM considers memory to be insufficient, it will reclaim the objects pointed to by soft references before throwing an OutOfMemoryError.

  • Weak Reference: Whenever the JVM performs garbage collection, regardless of whether memory is sufficient, it will reclaim the objects associated with weak references.

  • Phantom Reference: Also known as ghost reference or phantom reference, it is the weakest type of reference. The existence of a phantom reference does not affect the lifetime of an object. It merely provides a mechanism to perform certain actions after an object has been finalized, typically used for a so-called post-mortem cleanup mechanism.

Determining Object Reachability#

If an object is not referenced by any other object or variable, it is considered an invalid object and needs to be reclaimed.

Reference Counting Method#

A counter is maintained in the object header; each time an object is referenced, the counter is incremented by 1; when a reference becomes invalid, the counter is decremented by 1. When the counter reaches 0, the object is considered invalid.

The implementation of the reference counting algorithm is simple, and its determination efficiency is high, making it a good algorithm in most cases. However, mainstream Java virtual machines do not use the reference counting algorithm for memory management, mainly because it is difficult to solve the problem of circular references between objects:
For example, if object objA and objB both have a field instance, setting objA.instance = objB and objB.instance = objA means they reference each other, resulting in their reference counts not being 0, so the reference counting algorithm cannot notify the GC collector to reclaim them.

Reachability Analysis Method#

All objects directly or indirectly associated with GC Roots are valid objects; objects not associated with GC Roots are invalid objects.

GC Roots refers to:

  • Objects referenced in the Java Virtual Machine stack (local variable table in stack frames)
  • Objects referenced in the native method stack
  • Objects referenced by constants in the method area
  • Objects referenced by class static properties in the method area

GC Roots does not include objects referenced by objects in the heap, thus avoiding the problem of circular references.

Reclaiming Invalid Objects in the Heap#

For objects that are unreachable in reachability analysis, there is still a possibility of survival.

Determining if finalize() Needs to Be Executed#

The JVM will determine whether it is necessary to execute the finalize() method for this object. If the object does not override the finalize() method or if the finalize() method has already been called by the virtual machine, it is considered "not necessary to execute." In this case, the object is essentially reclaimed.

If the object is determined to need the finalize() method to be executed, it will be placed in an F-Queue queue, and the virtual machine will execute these finalize() methods with lower priority, but it will not ensure that all finalize() methods will finish executing. If the finalize() method contains time-consuming operations, the virtual machine will directly stop pointing to that method and clear the object.

Object Rebirth or Death#

If during the execution of the finalize() method, this is assigned to a reference, then the object is reborn. If not, it will be cleared by the garbage collector.

Note:
The finalize() method of any object will only be automatically called by the system once. If the object faces another collection, its finalize() method will not be executed again, and attempts to self-rescue in finalize() will fail.

Reclaiming Memory in the Method Area#

The method area stores class information, constants, and static variables with longer lifetimes, and only a small amount of garbage is cleared during each garbage collection. The method area mainly clears two types of garbage:

  • Abandoned constants
  • Useless classes

Determining Abandoned Constants#

As long as the constants in the constant pool are not referenced by any variable or object, these constants will be cleared. For example, if a string "bingo" enters the constant pool, but currently no String object references the "bingo" constant in the constant pool, and there are no other references to this literal, if necessary, the "bingo" constant will be cleaned out of the constant pool.

Determining Useless Classes#

Determining whether a class is a "useless class" has strict conditions.

  • All objects of the class have been cleared.
  • The ClassLoader that loaded the class has been reclaimed.
  • The java.lang.Class object of the class is not referenced anywhere, making it impossible to access the class's methods through reflection.

When a class is loaded into the method area by the virtual machine, an object representing that class is created in the heap. This object is created when the class is loaded into the method area and is cleared when the class is deleted from the method area.

Memory Leak#

In Java, if a long-lived object holds a reference to a short-lived object, it is very likely to cause a memory leak. For example, in the singleton pattern, we can often consider the lifecycle of this singleton object to be similar to that of the entire program, making it a long-lived object. If this object holds references to other objects, a memory leak may occur.

For more detailed content, please refer to: Detailed Explanation of JAVA Memory Leak (Causes, Examples, and Solutions)

2. Garbage Collection Algorithms#

After learning how to determine invalid objects, useless classes, and abandoned constants, the remaining task is to reclaim this garbage. Common garbage collection algorithms include the following:

Mark-and-Sweep Algorithm#

Determine which data needs to be cleared, mark them, and then clear the marked data.

This method has two disadvantages:

  • Efficiency issue: The efficiency of both the marking and clearing processes is not high.
  • Space issue: Mark-and-sweep can produce a large amount of discontinuous memory fragmentation, and too much fragmentation may lead to the inability to find enough contiguous memory for larger object allocations, necessitating an early trigger of another garbage collection action.

Copying Algorithm (Young Generation)#

To solve the efficiency problem, the "copying" collection algorithm was introduced. It divides the available memory into two equal-sized blocks, using only one block at a time. When this block runs out of memory and garbage collection is needed, the surviving objects are copied to the other block, and the first block is completely cleared. This algorithm has its pros and cons:

  • Advantages: There is no problem of memory fragmentation.
  • Disadvantages: Memory is reduced to half the original size, wasting space.

To solve the space utilization problem, memory can be divided into three blocks: Eden, From Survivor, and To Survivor, with a ratio of 8:1:1, using Eden and one of the Survivor blocks each time. During collection, the surviving objects in Eden and Survivor are copied to the other Survivor space at once, and then Eden and the recently used Survivor space are cleaned up. This way, only 10% of the memory is wasted.

However, we cannot guarantee that each collection will have no more than 10% of the objects surviving. When the Survivor space is insufficient, it needs to rely on other memory (the old generation) for allocation guarantees.

Allocation Guarantee#

By clearing abandoned data in the old generation to expand the free space in the old generation, allocation guarantees can be provided for the young generation.

Before JDK 6 Update 24:
Before a Minor GC occurs, the virtual machine will first check whether the maximum available contiguous space in the old generation is greater than the total space of all objects in the young generation. If this condition is met, Minor GC can be ensured to be safe; if not, the virtual machine will check whether the HandlePromotionFailure value is set to allow guarantee failure. If it is, it will continue to check whether the maximum available contiguous space in the old generation is greater than the average size of objects promoted to the old generation in previous collections. If it is, it will attempt a Minor GC, even though this Minor GC is risky; if it is less than that, or if HandlePromotionFailure is set to not allow risks, it will switch to performing a Full GC.

After JDK 6 Update 24:
As long as the contiguous space in the old generation is greater than the total size of young generation objects or the average size of previous promotions, a Minor GC will occur; otherwise, a Full GC will be performed.

This process is called allocation guarantee.

Mark-and-Compact Algorithm (Old Generation)#

Before reclaiming garbage, first mark the abandoned objects, then move the unmarked objects to one side, and finally clear the other side.

This is a garbage collection algorithm for the old generation. Objects in the old generation generally have longer lifetimes, so there are usually many surviving objects during garbage collection. If the copying algorithm is used, a large number of surviving objects need to be copied each time, which is inefficient.

Generational Collection Algorithm#

Based on the different lifecycles of objects, memory is divided into several blocks. Generally, the Java heap is divided into young generation and old generation, and the most appropriate collection algorithm is used for each generation's characteristics.

  • Young Generation: Copying Algorithm
  • Old Generation: Mark-and-Sweep Algorithm, Mark-and-Compact Algorithm

3. Garbage Collectors#

image.png

Young Generation Garbage Collectors#

Serial Garbage Collector (Single-threaded)#

Only one GC thread is opened for garbage collection, and all user threads are stopped during the garbage collection process (Stop The World).

Generally, client applications require less memory, do not create too many objects, and the heap memory is not large, so the garbage collector's collection time is short. Even during this time, stopping all user threads does not result in noticeable stuttering. Therefore, the Serial garbage collector is suitable for client use.

Since the Serial collector only uses one GC thread, it avoids the overhead of thread switching, making it simple and efficient.

ParNew Garbage Collector (Multi-threaded)#

ParNew is the multi-threaded version of Serial. It uses multiple GC threads to perform garbage cleanup in parallel. However, the cleanup process still requires Stop The World.

ParNew pursues "low pause time," and its only difference from Serial is that it uses multiple threads for garbage collection. In a multi-CPU environment, its performance is somewhat better than Serial; however, thread switching incurs additional overhead, so it performs worse than Serial in a single CPU environment.

Parallel Scavenge Garbage Collector (Multi-threaded)#

Parallel Scavenge, like ParNew, is also a multi-threaded young generation garbage collector. However, there are significant differences between the two:

  • Parallel Scavenge: Pursues CPU throughput and can complete specified tasks in a shorter time, making it suitable for non-interactive background calculations.
  • ParNew: Aims to reduce user pause time, making it suitable for interactive applications.

Pursuing high throughput can be achieved by reducing the time spent on actual work during GC execution. However, running GC only occasionally means that there will be a lot of work to do each time it runs, as many objects accumulate in the heap during this time. A single GC will take more time to complete, leading to longer pause times. Conversely, to achieve low pause times, it is better to run GC frequently to complete it more quickly, which in turn leads to a decrease in throughput.

  • Use the parameter -XX:GCTimeRatio to set the percentage of garbage collection time in total CPU time.
  • Use the parameter -XX:MaxGCPauseMillis to set the maximum pause time during garbage processing.
  • Use the command -XX:+UseAdaptiveSizePolicy to enable adaptive policy. As long as we set the heap size and MaxGCPauseMillis or GCTimeRatio, the collector will automatically adjust the size of the young generation, the ratio of Eden and Survivor, and the age at which objects enter the old generation to maximize proximity to our set MaxGCPauseMillis or GCTimeRatio.

Old Generation Garbage Collectors#

Serial Old Garbage Collector (Single-threaded)#

The Serial Old collector is the old generation version of Serial, both being single-threaded collectors that only enable one GC thread, suitable for client applications. Their only difference is that Serial Old works in the old generation using the "mark-and-compact" algorithm, while Serial works in the young generation using the "copying" algorithm.

Parallel Old Garbage Collector (Multi-threaded)#

The Parallel Old collector is the old generation version of Parallel Scavenge, pursuing CPU throughput.

CMS Garbage Collector#

The CMS (Concurrent Mark Sweep) collector is designed to achieve the shortest collection pause time (pursuing low pause). It allows user threads and GC threads to execute concurrently during garbage collection, so users do not feel significant stuttering during the process.

The CMS collector implements a "mark-and-sweep" algorithm, and the entire process is divided into four steps:

  • Initial Mark: Stop The World, using only one initial marking thread to mark all objects directly associated with GC Roots.
  • Concurrent Mark: Using multiple marking threads to execute concurrently with user threads. This process performs reachability analysis and marks all abandoned objects. It is slow.
  • Remark: Stop The World, using multiple marking threads to execute concurrently, marking newly abandoned objects that appeared during the concurrent marking process.
  • Concurrent Sweep: Using only one GC thread to execute concurrently with user threads, clearing the previously marked objects. This process is very time-consuming.

The concurrent marking and concurrent sweeping processes take the longest time and can work alongside user threads, so overall, the memory reclamation process of the CMS collector is executed concurrently with user threads.

Disadvantages

  • Sensitive to CPU resources, low throughput;
  • Cannot handle floating garbage, leading to frequent Full GC;
  • The collection algorithm it uses - "mark-and-sweep" - can lead to a large amount of space fragmentation at the end of the collection.

G1 Garbage Collector#

G1 (Garbage-First) is a garbage collector aimed at server applications. It does not have the concept of young and old generations but divides the heap into independent regions. The G1 collector maintains a priority list in the background and, when performing garbage collection, first estimates the amount of garbage in each region, then prioritizes the regions with the highest reclamation value based on the allowed collection time. This method of dividing memory space into regions and prioritizing region reclamation ensures that the G1 collector can achieve as high a collection efficiency as possible within a limited time (breaking memory into pieces).

Overall, G1 is a collector based on the "mark-and-compact" algorithm, while locally (between two regions), it is based on the "copying" algorithm, meaning that no memory fragmentation occurs during operation.

Each region has a Remembered Set, which records the regions of all objects referenced in that region. When performing reachability analysis, simply adding the Remembered Set to GC Roots prevents the need to traverse the entire heap memory. Therefore, even if an object and the objects it references may not be in the same region, during garbage collection, it is not necessary to scan the entire heap memory to perform a complete reachability analysis.

If we do not count the maintenance of the Remembered Set, the work process of the G1 collector can be divided into the following steps:

  • Initial Mark: Stop The World, using only one initial marking thread to mark all objects directly associated with GC Roots.
  • Concurrent Mark: Using one marking thread to execute concurrently with user threads. This process performs reachability analysis and is slow.
  • Final Mark: Stop The World, using multiple marking threads to execute concurrently.
  • Filtering Reclamation: Reclaim abandoned objects, which also requires Stop The World and uses multiple filtering reclamation threads to execute concurrently.

Additional Expansion#

Situations that Trigger JVM to Perform Full GC#

Calling System.gc() Method#

This method call is a suggestion for the JVM to perform Full GC; note that this is only a suggestion and not a certainty, but in many cases, it will trigger Full GC, thereby increasing the frequency of Full GC. Generally, we only need to let the virtual machine manage memory on its own, and we can disable the call to System.gc() using -XX:+DisableExplicitGC.

Insufficient Old Generation Space#

Insufficient old generation space will trigger Full GC operations. If space remains insufficient after this operation, the following error will be thrown: java.lang.OutOfMemoryError: Java heap space.

Insufficient Permanent Generation Space#

In the JVM specification, the method area in the runtime data area is also known as the permanent generation (Permanent Generation) in the HotSpot virtual machine, which stores some class information, constants, static variables, etc. When there are many classes to be loaded, reflected, and methods called, the permanent generation may become full, triggering Full GC. If Full GC still cannot reclaim space, the JVM will throw the following error message: java.lang.OutOfMemoryError: PermGen space.

Others#

  • Promotion failed and concurrent mode failure during CMS GC
    Promotion failed refers to the allocation guarantee failure mentioned above, while concurrent mode failure occurs when objects need to be placed into the old generation during CMS GC, but there is insufficient space in the old generation.

  • The average size of Minor GC promotions to the old generation is greater than the remaining space in the old generation.

Loading...
Ownership of this post data is guaranteed by blockchain and smart contracts to the creator alone.