Introduction to Java Garbage Collection

file

Overview

Version context: this collection of notes contains historical HotSpot examples. The generational walkthrough and legacy flag table are labeled as such. Modern collector choices and defaults depend on the JDK, vendor and platform; use version-specific documentation.

Definition

The garbage collector reclaims eligible objects that are no longer reachable from GC roots. An unreachable cycle can be collectible even though its objects still reference one another.

Background reference: (https://www.scaler.com/topics/media/garbage-collection-roots-in-java-1024×413.webp)
Reachability begins at GC roots

Where are the Java Garbage Collectors

The GC implementation lives in the JVM.
Background reference: https://binarycoders.files.wordpress.com/2021/04/jvm_architecture.png
JVM overview

Runs on the Heap or the Stack?

Java GC primarily manages object-heap memory. Stack frames are reclaimed as calls return; references in live stack frames may keep heap objects reachable.

Garbage

An object becomes eligible for collection when it is no longer reachable under the collector’s reference-processing rules. Nulling one variable or reassigning it is not enough if another live path still reaches the object.

  • By nulling the reference
  • By assigning a reference to another
  • By anonymous object etc.

Nulling a Reference

Employee e=new Employee();
e=null;

Assigning a Reference to Another

Employee e1=new Employee();
Employee e2=new Employee();
e1=e2;
// Now the first object referred to by 'e1' is available for garbage collection

Create an Anonymous Object

new Employee();

Pros and Cons of GC

Pros

  1. GC frees the programmer from manually deallocating memory.
  2. GC helps avoid these errors:

1/3 Dangling pointers occur when a piece of memory is freed while there are still pointers to it, and one of those pointers is dereferenced. By then, the memory may have been reassigned to another use, with unpredictable results.

Background reference: https://static.javatpoint.com/cpages/images/dangling-pointers-in-c.png
A native pointer still points at a freed allocation; dereferencing it is invalid.

2/3 Double free bugs occur when the program tries to free a region of memory that has already been freed and perhaps already been allocated again.

3/3 Some types of memory leaks , in which a program fails to free memory occupied by objects that have become unreachable, which can lead to memory exhaustion.

Background reference: https://www.baeldung.com/wp-content/uploads/2018/11/Memory-_Leak-_In-_Java.png
A reachable object can still be unwanted

Cons

Using Computing Resources – GC uses computing resources and brings some runtime overhead that is out of the programmer’s control. GC could lead to performance problems for large applications.

Some collection phases pause application threads; concurrent collectors perform other work alongside the application. Concurrent collection does not mean that all pauses disappear.

Unpredictable – The moment the garbage is collected can be unpredictable. Unpredictable stalls can be unacceptable in real-time environments.

Measures of the GC Performance

The two primary measures of garbage collection performance are throughput and pauses.

Two other considerations are footprint and promptness.

Throughput

Throughput measures useful application work per unit time. GC discussions also use the fraction of time outside stop-the-world collection as a simplified measure; concurrent GC can consume CPU while application threads are running.

In a simplified non-overlapping model, spending 10% of elapsed time in GC leaves 90% for application execution. This arithmetic does not directly describe a concurrent collector’s CPU overhead.

The right target depends on the workload: throughput, tail latency and memory use must be balanced against application requirements.

Pauses / Latency

Pauses are times when an application appears unresponsive due to GC.

GC pause duration is one contributor to application latency; queuing, I/O, locks and CPU contention can matter too.

Interactive applications often have pause or tail-latency budgets. Minimizing a single GC metric is not automatically the best overall design.

Footprint

Memory footprint can refer to reserved, committed or resident process memory; name the metric being measured rather than treating these as interchangeable.

The JVM process includes more than the Java heap: class metadata, thread stacks, compiled code and native allocations also consume memory.

Optimizing footprint means storing fewer things in memory for less time (do not load what you do not need, free memory as soon as possible, i.e., do not keep a reference on no more used variable).

Reducing footprint may come with an additional overhead in CPU or other IO (memory is quick to access much more than file system or network).

-Xmx limits the Java heap, not total process memory. A smaller heap can increase collection pressure or cause allocation failure; size it using measurements rather than assuming that smaller is always better.

A good example of footprint reduction would be processing an XML tree using a pull parser (parse the tree chunk by chunk) instead of DOM (load the whole tree in memory to process it)

Promptness

Promptness is the time between when an object becomes dead and when the memory becomes available. Promptness is an important consideration for distributed systems.

Trade-Off of Measures

Generation sizes can affect throughput, pauses, memory use and reclamation delay. The trade-off depends on object lifetimes, allocation rate and the collector.

For example, a large young generation likely maximizes throughput but at the cost of footprint and promptness.

A smaller young generation may shorten individual collections while causing them more often; it does not guarantee lower application latency or better promptness.

GC Algorithms – Marking Reachable Objects

The most traditional approaches are tracing and reference counting:

Tracing

Recursively mark reachability by starting from a set of root memory blocks in use (e.g., pointed to by global or local variables currently in stack frames).

Example: (where E, G, and H will be cleaned up )

Background reference: https://en.wikipedia.org/wiki/Tracing_garbage_collection

Reference Counting

Count the number of pointers pointing to one particular object by bookkeeping it every time a pointer is created or modified. It frees the object when the counter decreases to zero.

Background reference: https://plumbr.io/app/uploads/2015/05/Java-GC-counting-references1.png
An unreachable pair of objects can retain positive reference counts by referring to each other.

Tracing vs. Reference Counting

Property Tracing Basic reference counting
Starting point Reachability from roots Counts maintained as references change
Unreachable cycles Can collect them Needs an additional cycle-handling technique
Mutation overhead Can include barriers and bookkeeping Includes count maintenance
Pauses and real-time behavior Depend on the collector and scheduling Reclamation cascades can also take time; not inherently a hard real-time guarantee

Java’s Choice

The JVM uses Tracing.

GC Algorithms – Removing Unused Objects

Mark and Sweep

Mark and Sweep – Just remove unused objects

Sweeping frees dead objects; compaction gathers survivors and closes the gaps.

Fragmenting Issue

Sweeping frees unreachable objects but may leave holes between survivors. Compaction or copying can turn that scattered space into more usable allocation regions.

Issues:

  • Write operations become more time-consuming as finding the next free block of sufficient size is hard.
  • An allocation needs space that satisfies the collector’s layout rules. Fragmentation can require compaction, another region or heap growth; failure occurs if the runtime cannot satisfy the allocation.

Solution: Compacting / Copying

Mark-Sweep-Compact

This compacting process relocates all the reachable objects next to each other.

Original schematic created for these notes.

Pros and Cons

Pros
Eliminating (or reducing) the fragmentation – after such a compacting operation, new object allocation is again extremely cheap via pointer bumping.

Using such an approach, the location of the free space is always known, and no fragmentation issues are triggered either.

Cons

Moving live objects and updating references has a cost. How much work is paused or concurrent depends on the collector; not every collection moves every object.

Mark-Copy

Mark and Copy algorithms are very similar to the Mark and Compact as they, too, relocate all live objects. The important difference is that the relocation target is a different memory region as a new home for survivors.

Pros and Cons

Pros
Copying can occur simultaneously with marking during the same phase.

Cons

The need for one more memory region, which should be large enough to accommodate survived objects.

Live objects are copied to a new region and their references are updated.

Usage Example

Serial GC – This collection of garbage collectors uses mark-copy for the Young Generation and mark-sweep-compact for the Old Generation.

Conceptual collection phases

The next illustration separates marking, reclaiming and compacting for explanation. A particular minor or concurrent collection need not execute these as three separate phases.

Step 1: Marking

Marking is where the garbage collector identifies which pieces of memory are in use and which are not.

Scanning can be time-consuming if GC must scan all objects in a system.

Background reference: https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/images/gcslides/Slide3

Mark, sweep and compact

Step 2: Deletion

Sweeping reclaims unreachable objects, leaving free regions between surviving objects. References from other unreachable objects do not make an object live.

Mark, sweep and compact

The memory allocator holds references to blocks of free space where a new object can be allocated.

Step 3: Compacting

Moving referenced objects together makes new memory allocation much easier and faster.

Mark, sweep and compact

Generations

Motivation

Performance Issue

Having to mark and compact all the objects in a JVM is inefficient. As more and more objects are allocated, the list of objects grows, leading to longer garbage collection time.

Most Objects are Short-Lived / Weak Generational Hypothesis

Empirical analysis of applications has shown that most objects are short-lived.

Y axis – the number of bytes allocated
X axis – the number of bytes allocated over time.

Fewer and fewer objects remain allocated over time. Many workloads allocate numerous short-lived objects. The schematic illustrates that motivation for generational collection; it is not a measured survival distribution.

Many objects die young: a qualitative model

These observations come together in the Weak Generational Hypothesis.

JVM Generation Categories

The following walkthrough uses a classic HotSpot generational model. Many ordinary new heap objects begin in Eden, survivors may move between survivor spaces, and long-lived objects may be promoted to the old generation. Large objects, allocation elimination and other collectors can follow different paths.

HotSpot replaced PermGen with native-memory Metaspace in Java 8. Metaspace holds class metadata; it is not an Eden, Survivor or Old object-heap generation. Region-based collectors such as G1 organize the heap differently from a fixed contiguous layout.

A classic object heap has Eden, two survivor spaces and Old; native Metaspace is separate.

The Generational GC Process

Step 1: Create Objects

In this simplified generational example, ordinary new heap objects begin in Eden and the survivor spaces initially contain no survivors. This is not a rule for every allocation or collector.

1 · Allocate young objects

Step 2: Trigger Minor GC

When the Eden space fills up, a minor garbage collection is triggered.

2 · A young collection is requested

Step 3: Move Objects to the Survivor Space

Next to the Eden space reside two Survivor spaces called from and to. It is important to notice that one of the two Survivor spaces is always empty.

Referenced objects are moved to the first survivor space. Unreferenced objects are deleted when the Eden space is cleared.

3 · Copy surviving young objects

The same thing happens at the next minor GC for the Eden space.

Unreferenced objects are deleted.
Referenced objects are moved to a survivor space (S1).

Step 4: Objects Aging

In addition, objects from the last minor GC on the first survivor space (S0) have their age incremented and get moved to S1. Once all surviving objects have been moved to S1, both S0 and Eden are cleared.

Notice that we now have differently aged objects in the survivor space.

4 · Survivors move and age

At the next minor GC, the same process repeats. However, this time the survivor spaces switch. Referenced objects are moved to S0. Surviving objects are aged. Eden and S1 are cleared.

5 · Survivor roles alternate

Step 5: Promotion

After a minor GC, when aged objects reach a certain age threshold (8 in this example), they are promoted from the young to the old.

6 · Some survivors are promoted

As minor GCs continue to occur, objects will continue to be promoted to the old generation’s space.

6 · Some survivors are promoted

So that covers the entire process with Young Generation. Old-generation collection eventually reclaims eligible old objects; compaction depends on the collector and collection mode.

Generational GC Process Summary

7 · Old-generation collection

Minor GC vs. Major GC vs. Full GC

It should be noted that there are no formal definitions for those terms – neither in the JVM specification nor in the Garbage Collection research papers.

Common conventions, subject to collector-specific log terminology:

  1. Full garbage clean the entire Heap – both Young and Old spaces.
  2. Major garbage collection works on the Old space.
  3. Minor garbage collection works on the Eden space.

GC Observations

Logging

JDK 9 and later use unified logging. For a basic view, run an application with -Xlog:gc; -Xlog:gc* selects more GC-related details. Check the command documentation for the JDK being used.

java -Xlog:gc MyApp

Read the collector name, event type, before/after occupancy and duration in context. “Minor,” “major,” “mixed” and “full” are not interchangeable portable labels across every collector. Old PrintGCDetails examples do not define the format of modern unified logs.

VisualVM

We can observe GC collection in Java VisualVM (see Oracle’s official doc – Performing Your Observations )
Read GC behavior from actual telemetry

Java Garbage Collector Categories

The Serial Garbage Collector

Definition

Both minor and major garbage collections are done serially (using a single virtual CPU).

It is the default for client-style machines in Java SE 5 and 6.

Serial and parallel GC can pause applications; concurrent phases overlap application work but do not eliminate all pauses.

Clean Up Algorithm

Young Generation: mark-copy
Old Generation: mark-sweep-compact

Serialization

It uses the only thread for garbage collection.

It works by holding all the threads of an application. It means that threads of the application freeze by the serial garbage collector during the garbage collection process, and the process is known as stop the world event.

Usage Cases

Serial Garbage collector is well-matched for

  1. single-threaded environments.
  2. where many JVMs are run on the same machine (in some cases, more JVMs than available processors). In such environments, when a JVM does a garbage collection, it is better to use only one processor to minimize the interference on the remaining JVMs, even if the garbage collection might last longer. Furthermore, the Serial GC fits this trade-off nicely.
  3. We need Serial GC to return the proliferation of embedded hardware with minimal memory and cores.

The Parallel Garbage Collector

Definition

The Parallel collector uses multiple GC workers for throughput-oriented, stop-the-world collection. Worker counts are selected by JVM ergonomics and can be configured; they are not always identical to the number of CPUs.

Clean Up Algorithm

Young Generation: mark-copy
Old Generation: mark-sweep-compact

Stop the World

Young and Old collections trigger stop-the-world events, stopping all application threads from performing garbage collection.

Parallelization

  • During its parallel phases, multiple GC workers cooperate while application threads are paused. This can reduce collection time, but does not guarantee that every core is used or that every workload has shorter pauses.
  • Between garbage collection cycles, neither of the collectors is consuming any resources

Usage Cases

The Parallel Garbage Collector is usually used on multi-core machines when your primary goal is to increase throughput. Higher throughput is achieved due to more efficient usage of system resources.

Example: printing reports or bills or performing many database queries.

Arg for the Number of Threads

_-XX:ParallelGCThreads=NNN_ . 

Worker defaults depend on the JDK, available processors and runtime ergonomics. Consult the version-specific ParallelGCThreads documentation.

The (Mostly) Concurrent Mark Sweep (CMS) Garbage Collector

Definition

The Concurrent Mark Sweep (CMS) collector collects the tenured generation. It attempts to minimize the pauses due to garbage collection by doing most garbage collection work concurrently with the application threads.

  • Normally, the concurrent low pause collector does not copy or compact the live objects.
  • Garbage collection is done without moving the live objects.
  • For the historical CMS collector, fragmentation could trigger expensive fallback behavior. Increasing the heap was not a universal fix; CMS is no longer available in JDK 14 and later.

Historical HotSpot CMS configurations commonly paired old-generation CMS with the young-generation ParNew collector; do not equate every such configuration with Parallel GC.

Note: The official name for this collection of garbage collectors is “Mostly Concurrent Mark and Sweep Garbage Collector.”

Concurrency vs. Parallelism

Parallel and concurrent describe different things

Motivation

The CMS was designed to avoid long pauses while collecting in the Old Generation.

  1. it does not compact the Old Generation but uses free lists to manage reclaimed space.
  2. it does most of the job in the mark-and-sweep phases concurrently with the application.

This means that garbage collection is not explicitly stopping the application threads from performing these phases. It should be noted, however, that it still competes for CPU time with the application threads.

Historical CMS worker ergonomics varied by release and processor configuration. There is no portable “one quarter of physical cores” rule for all JVMs.

Clean Up Algorithm

Young Generation: mark-copy
Old Generation: mark-sweep (normally non-compacting)

Usage Cases

Historically, the CMS collector was used for applications requiring low pause times and share resources with the garbage collector.

Examples include desktop UI applications that respond to events, a web server responding to requests, or a database responding to queries.

The Garbage First (G1) Garbage Collector

Definition

The Garbage First or G1 garbage collector is available in Java 7 and is designed to be the long-term replacement for the CMS collector.

The G1 collector is a parallel, concurrent, and incrementally compacting low-pause garbage collector with quite a different layout from the previous garbage collectors.

Design

One of the key design goals of G1 was to make the duration and distribution of stop-the-world pauses due to garbage collection predictable and configurable.

  1. Unlike a fixed contiguous young/old split, G1 divides the heap into regions. Eden and Survivor regions form the logical young generation; Old regions form the logical old generation. The counts and region sizes are selected dynamically within implementation constraints.
  2. It estimates the amount of live data that each region contains. This is used in building the collection set: the regions that contain the most garbage are collected first. Hence the name: garbage-first collection.

G1 regions are assigned logical Eden, Survivor, Old or free roles.

Usage

The G1 collector can achieve high throughput and low latency for applications that meet one or more of the following criteria:

  • Large heap size: More than 6GB where live objects occupy more than 50%.
  • Rates of allocation and promotion between garbage collection generations may vary significantly during the application’s run.
  • A large amount of fragmentation in a heap.
  • An application pause target that must be validated on its actual workload; a configured goal is not a hard maximum guarantee.

GC in Java

Java’s collector manages eligible heap objects. It does not replace explicit ownership of files, sockets, database connections or native allocations. C++ also has automatic lifetime mechanisms such as RAII and smart pointers; the distinction is not simply “Java automatic, C++ always manual.”

Finalization is not reliable resource management

Do not assume every object has a finalizer, that a finalizer runs before program exit, or that a single GC daemon calls it. Finalization was deprecated for removal by JEP 421 in JDK 18. Prefer AutoCloseable and try-with-resources for resources that need deterministic release.

Collection is about reachability, not source syntax

GC is not restricted to objects explicitly created with the new keyword. Arrays, strings, reflective construction and other language/library operations can create managed objects. A finalize() method is not a general-purpose way to destroy objects created “without new.”

Can we force a collection?

System.gc() and Runtime.getRuntime().gc() request collection effort. They do not promise that a particular object will be reclaimed immediately, and a JVM can be configured to ignore explicit requests. Do not use them as a substitute for fixing retention or closing resources.

Legacy collector flag reference

The following combinations are historical Java 8-era notes, not a configuration guide for a current JDK. CMS was removed in JDK 14; some other flags below were removed or changed as well.

Young Tenured JVM options
Incremental Incremental -Xincgc
Serial Serial -XX:+UseSerialGC
Parallel Scavenge Serial -XX:+UseParallelGC -XX:-UseParallelOldGC
Parallel New Serial N/A
Serial Parallel Old N/A
Parallel Scavenge Parallel Old -XX:+UseParallelGC -XX:+UseParallelOldGC
Parallel New Parallel Old N/A
Serial CMS -XX:-UseParNewGC -XX:+UseConcMarkSweepGC
Parallel Scavenge CMS N/A
Parallel New CMS -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
G1 -XX:+UseG1GC

Heap and metadata options: check the JDK version

-Xms and -Xmx concern the Java heap. -XX:PermSize and -XX:MaxPermSize are pre-Java-8 PermGen options, not modern Metaspace settings. Young-generation sizing options are collector-specific.

-Xms

Sets the initial heap size for when the JVM starts.

-Xmx

Sets the maximum heap size.

-Xmn

Sets the size of the Young Generation.

-XX:PermSize

Sets the starting size of the Permanent Generation.

-XX:MaxPermSize

Sets the maximum size of the Permanent Generation

GC Code Example

public class TestGarbage1{
    public void finalize(){System.out.println("The object is garbage collected.");}
    public static void main(String args[]){
        TestGarbage1 s1=new TestGarbage1();
        TestGarbage1 s2=new TestGarbage1();
        s1=null;
        s2=null;
        System.gc();
    }
}

Result

The object is garbage collected.
The object is garbage collected.

OutOfMemoryError: GC overhead limit exceeded.

The GC overhead limit exceeded message means a collector’s overhead policy detected excessive collection effort with insufficient progress. It does not imply that all operating-system memory has been exhausted.

Why

Older HotSpot documentation describes an overhead-limit heuristic involving more than roughly 98% GC time and less than 2% recovered heap. This is collector- and release-specific, not a universal definition or threshold for every JVM.

Background reference: https://plumbr.io/app/uploads/2014/04/OOM-example-schema3.png
Repeated GC can reclaim little memory when much of the heap remains live.

What Would Happen

Note that the java.lang.OutOfMemoryError: GC overhead limit exceeded error is based on collector-specific lack-of-progress heuristics; it is not triggered by exactly 2% being freed

When the live set nearly fills the available heap, a small amount of reclaimed space may be consumed quickly and trigger another collection. The application can become extremely slow; inspect logs and retention evidence rather than assuming one fixed CPU percentage.

Example

In the following example, we create a “GC overhead limit exceeded” error by initializing a Map and adding key-value pairs into the map in an unterminated loop:

class Wrapper {
    public static void main(String args[]) throws Exception {
        Map map = System.getProperties();
        Random r = new Random();
        while (true) {
            map.put(r.nextInt(), "value");
        }
    }
}

Solution

The Bad Solution

Adding the following to disable the error check

-XX:-UseGCOverheadLimit

It will only result in another kind of error message, i.e., heap related.

Good Solutions

Investigate unbounded retention and unnecessary allocation first. Reduce the live set or allocation rate where measurements justify it; adding object pools indiscriminately can make retention and GC behavior worse.

If the live set is legitimate and the environment has capacity, review -Xmx with measured heap and native-memory requirements. More heap does not fix a leak.

Compare supported collectors using representative workloads and latency/throughput goals. Do not copy the obsolete CMS flag into a modern JDK.

References

For current behavior, start with the JDK 25 GC tuning guide, JEP 363 and JEP 421. The dated references below record the original study sources.

Java Garbage Collection Basics. (2022). Retrieved 4 October 2022, from https://www.oracle.com/webfolder/technetwork/tutorials/obe/java/gc01/index.html

Garbage collection (computer science) – Wikipedia. (2014). Retrieved 4 October 2022, from https://en.wikipedia.org/wiki/Garbage_collection_(computer_science)

Java Garbage Collection – javatpoint. (2022). Retrieved 4 October 2022, from https://www.javatpoint.com/Garbage-Collection

A Unified Theory of Garbage Collection. (2019). Retrieved 4 October 2022, from https://www.cs.cornell.edu/courses/cs6120/2019fa/blog/unified-theory-gc/

Reference Counting | Plumbr – User Experience & Application Performance Monitoring. (2015). Retrieved 4 October 2022, from https://plumbr.io/handbook/what-is-garbage-collection/automated-memory-management/reference-counting

GC overhead limit exceeded | Plumbr – User Experience & Application Performance Monitoring. (2014). Retrieved 13 November 2022, from https://plumbr.io/outofmemoryerror/gc-overhead-limit-exceeded

How to choose the best Java garbage collector | Red Hat Developer. (2021). Retrieved 11 November 2022, from https://developers.redhat.com/articles/2021/11/02/how-choose-best-java-garbage-collector#:~:text=Throughput%3A%20The%20percentage%20of%20total,running%205%25%20of%20the%20time.

Stages and levels of Java garbage collection | Red Hat Developer. (2021). Retrieved 11 November 2022, from https://developers.redhat.com/articles/2021/08/20/stages-and-levels-java-garbage-collection#

Java Garbage Collection – javatpoint. (2022). Retrieved 12 November 2022, from https://www.javatpoint.com/Garbage-Collection

Types of Garbage Collector in Java – Javatpoint. (2022). Retrieved 12 November 2022, from https://www.javatpoint.com/types-of-garbage-collector-in-java

Garbage Collection Interview Questions in Java – JavaTpoint. (2022). Retrieved 12 November 2022, from https://www.javatpoint.com/java-garbage-collection-interview-questions

Altvater, A. (2017). What is Java Garbage Collection? Best Practices, Tutorials & More. Retrieved 13 November 2022, from https://stackify.com/what-is-java-garbage-collection/

Garbage-First Garbage Collector. (2022). Retrieved 12 November 2022, from https://docs.oracle.com/javase/9/gctuning/garbage-first-garbage-collector.htm#JSGCT-GUID-F1BE86FA-3EDC-4D4F-BDB4-4B044AD83180

What is java.lang.OutOfMemoryError: GC overhead limit exceeded?. (2022). Retrieved 13 November 2022, from https://www.jvmhost.com/articles/what-is-java-lang-outofmemoryerror-gc-overhead-limit-exceeded/

collection, U., & Bogdan, D. (2018). Understanding footprint measurement in java garbage collection. Retrieved 14 November 2022, from https://stackoverflow.com/questions/48809234/understanding-footprint-measurement-in-java-garbage-collection

3 thoughts on “Introduction to Java Garbage Collection”

Leave a Reply

Your email address will not be published. Required fields are marked *