Java G1GC Interview Questions and Answers | G1 Garbage Collector Internals, Tuning and Production Scenarios

Master Java G1 Garbage Collector with real interview questions, region-based heap architecture, young and mixed collections, SATB, remembered sets, humongous objects, tuning options, GC logs, and production troubleshooting.


Introduction

The Garbage-First Garbage Collector, commonly called G1GC, is a region-based, generational Garbage Collector designed to provide a balance between:

  • Application throughput
  • Predictable pause times
  • Heap utilization
  • Garbage Collection overhead

Unlike traditional collectors that divide the heap into large contiguous young and old-generation sections, G1 divides the heap into many equally sized regions.

Each region can dynamically act as:

  • Eden
  • Survivor
  • Old
  • Humongous
  • Free

G1 attempts to reclaim regions containing the most garbage first. This behavior is the reason behind the name Garbage-First.

G1 became the default collector for server-class HotSpot configurations starting with JDK 9. It is designed for multiprocessor systems and applications using medium-to-large heaps.

G1GC is widely used in:

  • Spring Boot applications
  • Microservices
  • REST APIs
  • Banking systems
  • Insurance platforms
  • Kafka-based systems
  • Large enterprise applications
  • Containerized Java workloads

Learning Objectives

After completing this article, you will understand:

  • Why G1GC was introduced
  • Region-based heap architecture
  • Eden, Survivor, Old, and Humongous regions
  • Young-only collections
  • Concurrent marking cycles
  • Mixed Garbage Collections
  • Remembered Sets
  • Card Tables
  • Write Barriers
  • Snapshot-At-The-Beginning
  • Evacuation
  • Humongous objects
  • Pause-time prediction
  • Evacuation failure
  • Full GC in G1
  • Important JVM tuning options
  • GC log analysis
  • Production troubleshooting
  • Senior-level interview questions

Why Was G1GC Introduced?

Traditional collectors encounter challenges as heap sizes increase.

For example:

  • Serial GC uses only one GC thread.
  • Parallel GC provides high throughput but can create long Stop-The-World pauses.
  • CMS attempted concurrent collection but suffered from fragmentation and complicated failure modes.

G1 was designed to:

  • Work efficiently with larger heaps
  • Reduce long and unpredictable pauses
  • Perform incremental compaction
  • Reclaim high-garbage regions first
  • Balance latency and throughput
  • Avoid heap fragmentation

G1 does not guarantee an exact maximum pause time. Instead, it uses historical collection statistics and attempts to meet a user-configured pause-time goal with high probability.


Enabling G1GC

Use the following JVM option:

java -XX:+UseG1GC -jar application.jar

For modern server-class JDK configurations, G1 may already be selected automatically.

You can explicitly enable it when testing or standardizing production JVM configuration.


Verify the Active Garbage Collector

Use unified JVM logging:

java -Xlog:gc -version

For a running application:

jcmd <pid> VM.flags

You can also print JVM startup flags:

java -XX:+PrintCommandLineFlags -version

G1 Heap Architecture

G1 divides the Java heap into many equal-sized regions.

flowchart TD
    Heap["Java Heap"] --> R1["Eden Region"]
    Heap --> R2["Survivor Region"]
    Heap --> R3["Old Region"]
    Heap --> R4["Humongous Region"]
    Heap --> R5["Free Region"]
    Heap --> RN["Additional Regions"]

The regions do not need to appear in a fixed physical order.

For example, the heap may look like this:

+--------+--------+--------+--------+--------+--------+
| Eden   | Old    | Free   | Eden   | Surv.  | Old    |
+--------+--------+--------+--------+--------+--------+
| Humongous       | Free   | Old    | Eden   | Free   |
+-----------------+--------+--------+--------+--------+

This flexible layout enables G1 to resize generations dynamically without requiring large contiguous young and old-generation spaces.


What Is a G1 Region?

A region is the basic heap-management unit used by G1.

Instead of collecting the entire heap during every pause, G1 selects a set of regions called the Collection Set.

Only those selected regions are evacuated during that pause.

Conceptually:

Java Heap
   |
   +-- Region 1: Eden
   +-- Region 2: Old
   +-- Region 3: Free
   +-- Region 4: Survivor
   +-- Region 5: Old
   +-- Region 6: Humongous

The region size is selected ergonomically by the JVM based on the heap size.

It can also be configured explicitly:

-XX:G1HeapRegionSize=16m

Supported region sizes are powers of two, typically from 1 MB through 32 MB, with the JVM aiming for approximately 2,048 regions under common configurations.

Normally, you should allow the JVM to select the region size automatically.


G1 Region Types

Region type Purpose
Eden Stores newly created objects
Survivor Stores objects that survived a young collection
Old Stores long-lived or promoted objects
Humongous Start Stores the beginning of a very large object
Humongous Continuation Stores the remaining part of a large object
Free Available for future allocations

G1 Object Allocation Flow

Most normal objects are allocated in Eden regions.

flowchart LR
    Create["Create Object"] --> Eden["Eden Region"]
    Eden --> YoungGC["Young GC"]
    YoungGC --> Survivor["Survivor Region"]
    Survivor --> MoreGC["Additional Young GCs"]
    MoreGC --> Old["Old Region"]

The general lifecycle is:

  1. Allocate the object in Eden.
  2. Perform a Young GC when Eden fills.
  3. Copy live objects into Survivor or Old regions.
  4. Reclaim the original Eden regions.
  5. Reuse the newly freed regions.

What Is Evacuation?

G1 generally reclaims memory by copying live objects out of selected regions.

This process is called evacuation.

flowchart LR
    Source["Selected Region"] --> Live["Copy Live Objects"]
    Live --> Destination["Survivor or Old Region"]
    Source --> Reclaim["Reclaim Entire Source Region"]

For example:

Before evacuation:

Region A
+----------------------+
| Live | Dead | Live   |
+----------------------+

After evacuation:

Region B
+----------------------+
| Live | Live          |
+----------------------+

Region A becomes completely free.

This copying process provides incremental compaction and reduces fragmentation.


G1 Collection Types

G1 primarily performs:

  1. Young-only collection
  2. Concurrent marking cycle
  3. Mixed collection
  4. Full GC as a fallback

Young-Only Collection

A Young GC collects Eden and usually Survivor regions.

It is a Stop-The-World event.

flowchart TD
    Running["Application Running"] --> Pause["Stop-The-World"]
    Pause --> Eden["Collect Eden Regions"]
    Eden --> Survivor["Process Survivor Regions"]
    Survivor --> Copy["Copy Live Objects"]
    Copy --> Resume["Application Resumes"]

During a Young GC:

  • Application threads stop.
  • G1 selects young regions.
  • Live objects are copied to Survivor or Old regions.
  • Dead objects are discarded.
  • Empty regions return to the free-region pool.

Young collections do not normally collect old regions.


Concurrent Marking Cycle

As old-generation occupancy increases, G1 starts a concurrent marking cycle.

The purpose is to identify:

  • Which old regions contain live objects
  • Which old regions contain mostly garbage
  • Which old regions are good candidates for Mixed GC

Most of the marking work runs concurrently with application threads.


G1 Concurrent Marking Phases

The major phases are:

flowchart TD
    Start["Initial Mark"] --> RootScan["Concurrent Root-Region Scan"]
    RootScan --> Mark["Concurrent Mark"]
    Mark --> Remark["Remark"]
    Remark --> Cleanup["Cleanup"]
    Cleanup --> Mixed["Mixed Collections"]

1. Initial Mark

Initial Mark identifies objects directly reachable from GC roots.

Characteristics:

  • Stop-The-World
  • Usually piggybacked on a Young GC
  • Starts the concurrent marking cycle

2. Concurrent Root-Region Scan

G1 scans survivor regions that may contain references into the old generation.

Characteristics:

  • Runs concurrently
  • Must complete before the next Young GC
  • Helps identify old-generation reachability

3. Concurrent Mark

G1 traverses the object graph and marks reachable objects throughout the heap.

Characteristics:

  • Runs concurrently with application threads
  • Can consume CPU while the application is running
  • Determines liveness information for old regions

4. Remark

Remark completes marking and handles reference changes that occurred while Concurrent Mark was running.

Characteristics:

  • Stop-The-World
  • Uses Snapshot-At-The-Beginning information
  • Processes remaining marking work
  • Commonly processes reference-related cleanup

5. Cleanup

Cleanup calculates region-level liveness and prepares old regions as collection candidates.

Characteristics:

  • Contains short Stop-The-World work
  • May include concurrent cleanup work
  • Identifies completely empty regions
  • Prepares candidates for Mixed GC

What Is Snapshot-At-The-Beginning?

G1 uses the Snapshot-At-The-Beginning, or SATB, marking algorithm.

SATB conceptually considers objects reachable at the beginning of the marking cycle to be part of the live snapshot.

The application continues changing references while Concurrent Mark runs.

A write barrier records important reference changes so that objects that were reachable at the start are not accidentally missed.

Example:

Employee oldEmployee = department.getManager();

department.setManager(new Employee());

During concurrent marking, the reference to oldEmployee may be overwritten.

SATB-related barriers help preserve the information necessary for the collector to correctly complete marking.


SATB Conceptual Flow

flowchart LR
    Snapshot["Initial Object Graph"] --> Concurrent["Concurrent Marking"]
    Update["Application Changes Reference"] --> Barrier["SATB Write Barrier"]
    Barrier --> Queue["Record Old Reference"]
    Queue --> Concurrent
    Concurrent --> Result["Complete Live-Object View"]

SATB enables much of old-generation marking to happen concurrently with the application.


What Is Mixed Garbage Collection?

After a successful concurrent marking cycle, G1 may perform a sequence of Mixed GCs.

A Mixed GC collects:

  • All selected young regions
  • Selected old regions containing significant reclaimable space
flowchart TD
    CSet["Collection Set"] --> Eden["Eden Regions"]
    CSet --> Survivor["Survivor Regions"]
    CSet --> Old["Selected Old Regions"]
    Eden --> Evacuate["Evacuate Live Objects"]
    Survivor --> Evacuate
    Old --> Evacuate
    Evacuate --> Free["Reclaim Regions"]

It is called mixed because young and old regions are collected together.

G1 does not normally collect every old region in one pause. It distributes old-generation reclamation across multiple pauses to better control latency.


Why Is It Called Garbage-First?

After marking, G1 knows approximately how much live and reclaimable data each region contains.

It prioritizes regions expected to provide the greatest amount of reclaimed memory for an acceptable collection cost.

Conceptually:

Region Live data Garbage Collection priority
Region A 10% 90% High
Region B 40% 60% Medium
Region C 90% 10% Low

Regions containing more garbage are typically more attractive collection candidates.

This does not mean G1 always collects one region at a time. It builds a Collection Set containing multiple regions.


What Is a Collection Set?

A Collection Set, commonly shown as CSet in GC logs, is the group of regions selected for a particular Stop-The-World evacuation pause.

A Young GC Collection Set usually contains:

  • Eden regions
  • Survivor regions

A Mixed GC Collection Set contains:

  • Young regions
  • Selected old regions
Collection Set
   |
   +-- Eden Region 5
   +-- Eden Region 8
   +-- Survivor Region 11
   +-- Old Region 21
   +-- Old Region 29

The JVM attempts to select a Collection Set that fits within the configured pause-time goal.


What Is a Remembered Set?

Objects in one region may reference objects in another region.

Without additional metadata, G1 might need to scan the entire heap to discover references pointing into a region being collected.

To avoid this, G1 maintains Remembered Sets.

A Remembered Set tracks locations outside a region that may contain references into that region. This helps G1 evacuate selected regions without scanning the entire heap.

Example:

Old Region A  -------->  Eden Region B
Old Region C  -------->  Eden Region B

The Remembered Set for Region B contains information about references from Regions A and C.


Remembered Set Diagram

flowchart LR
    OldA["Old Region A"] --> YoungB["Young Region B"]
    OldC["Old Region C"] --> YoungB
    RSet["Remembered Set for B"] --> OldA
    RSet --> OldC

Remembered Sets improve collection efficiency but consume:

  • Additional memory
  • CPU for maintenance
  • Refinement processing time

What Is a Card Table?

The heap is divided into smaller logical units called cards.

When the application changes an object reference, a write barrier marks the corresponding card as dirty.

Background refinement threads process dirty cards and update remembered-set information.

flowchart LR
    Write["Reference Write"] --> Barrier["Write Barrier"]
    Barrier --> Dirty["Mark Card Dirty"]
    Dirty --> Queue["Dirty Card Queue"]
    Queue --> Refine["Concurrent Refinement"]
    Refine --> RSet["Update Remembered Set"]

This avoids rescanning every object after every reference update.


Write Barrier vs Load Barrier

G1 primarily relies on write barriers to:

  • Maintain remembered sets
  • Support SATB marking
  • Record cross-region references

A write barrier runs when application code updates references.

Low-latency collectors such as ZGC use different barrier strategies, including load barriers.


What Is Concurrent Refinement?

Application reference updates generate dirty-card information.

G1 refinement threads process those cards and update remembered sets concurrently.

If refinement threads cannot keep up:

  • Dirty-card queues grow
  • More card processing may move into GC pauses
  • Pause times can increase
  • Application threads may assist with refinement

Therefore, remembered-set and refinement behavior can be important when troubleshooting high G1 pause times.


What Is a Humongous Object?

An object is considered humongous when its size is at least half of a G1 region.

For example:

Region size: 8 MB
Humongous threshold: approximately 4 MB

Possible humongous objects include:

  • Large byte arrays
  • Large character arrays
  • Large JSON payloads
  • Large image buffers
  • Large in-memory file contents
  • Very large collections backed by arrays

Humongous objects are allocated directly in one or more contiguous humongous regions instead of normal Eden allocation.


Humongous Object Allocation

flowchart LR
    Object["Large Object"] --> Check{"Size >= Half Region?"}
    Check -->|No| Eden["Allocate in Eden"]
    Check -->|Yes| Humongous["Allocate in Humongous Regions"]

Example:

byte[] data = new byte[20 * 1024 * 1024];

Depending on the region size, this array may require several contiguous regions.

Humongous-object allocation can create pressure because:

  • Contiguous regions are required.
  • The final region may contain unused space.
  • Very frequent humongous allocations can trigger additional marking activity.
  • Humongous objects are more difficult and expensive to move.

Oracle's G1 documentation notes that humongous objects are only moved in a slow, last-resort collection path under extreme allocation pressure.


Humongous Region Layout

Large object requires three regions:

+----------------+----------------+----------------+
| Humongous Start| Continuation   | Continuation   |
+----------------+----------------+----------------+

The regions are treated as one object allocation.


Pause-Time Goal

The most common G1 tuning option is:

-XX:MaxGCPauseMillis=200

This tells G1 to attempt to keep GC pauses near the requested goal.

It is a soft goal, not a guarantee.

The JVM adjusts factors such as:

  • Young-generation size
  • Number of regions in the Collection Set
  • Old regions selected for Mixed GC
  • Evacuation workload

A lower pause target may result in:

  • Smaller collection sets
  • More frequent collections
  • Reduced throughput
  • Higher GC overhead

A higher pause target may allow:

  • Larger collection sets
  • Fewer collections
  • Better throughput
  • Longer individual pauses

G1 Pause Prediction

G1 records historical information about:

  • Region evacuation cost
  • Number of live objects
  • Remembered-set processing
  • Object-copying speed
  • Young-generation behavior

It uses this data to estimate how many regions can be processed within the pause-time goal.

flowchart LR
    History["Historical GC Data"] --> Predict["Pause Predictor"]
    Live["Region Liveness"] --> Predict
    RSet["Remembered Set Cost"] --> Predict
    Predict --> CSet["Build Collection Set"]
    CSet --> Pause["Execute GC Pause"]

Actual pauses can still exceed the target due to:

  • Large remembered sets
  • High object survival
  • Slow object copying
  • Humongous allocation
  • Operating-system scheduling
  • CPU throttling
  • Container resource limits
  • Evacuation failure

Initiating Heap Occupancy Percent

G1 starts concurrent marking based partly on heap occupancy and adaptive predictions.

A commonly discussed option is:

-XX:InitiatingHeapOccupancyPercent=45

This value is commonly abbreviated as IHOP.

It represents an occupancy threshold related to starting old-generation marking.

Modern G1 uses adaptive IHOP behavior by default, learning when marking must begin based on:

  • Allocation rate
  • Marking duration
  • Available heap reserve
  • Previous collection behavior

Setting a fixed IHOP value without evidence can make behavior worse.


What Is Evacuation Failure?

During a GC pause, G1 needs free regions to copy live objects.

An evacuation failure happens when sufficient destination space is unavailable.

Possible causes include:

  • Heap nearly full
  • Very high object survival rate
  • Concurrent marking started too late
  • Insufficient reserve
  • Allocation spike
  • Large humongous-object pressure
  • Memory leak

Consequences may include:

  • Longer GC pauses
  • Regions retained in place
  • Additional cleanup work
  • Full GC fallback
  • Possible OutOfMemoryError

What Is G1 Reserve Percent?

G1 keeps part of the heap as a reserve to reduce the risk of evacuation failure.

The relevant option is:

-XX:G1ReservePercent=10

Increasing the reserve can provide more evacuation headroom, but it also reduces the heap capacity available to normal application allocations.

Do not change this option without confirming evacuation pressure in GC logs.


Does G1 Perform Full GC?

Yes.

G1 is designed to avoid Full GC, but it can fall back to Full GC when normal concurrent and evacuation mechanisms cannot reclaim memory quickly enough.

Common causes include:

  • Evacuation failure
  • Concurrent mode failure-like conditions
  • Heap exhaustion
  • Humongous allocation pressure
  • Memory leak
  • Explicit System.gc()
  • Insufficient heap headroom

Modern G1 Full GC uses parallel worker threads following the implementation delivered through JEP 307.

A Full GC is generally:

  • Stop-The-World
  • Expensive
  • A sign requiring investigation when frequent
  • Capable of compacting the heap

G1GC Lifecycle

flowchart TD
    Allocate["Allocate Objects"] --> Young["Young-Only GC"]
    Young --> Check{"Old Occupancy Increasing?"}
    Check -->|No| Allocate
    Check -->|Yes| Mark["Concurrent Marking Cycle"]
    Mark --> Mixed["Mixed GC Sequence"]
    Mixed --> Reclaim["Reclaim Old Regions"]
    Reclaim --> Allocate
    Reclaim --> Pressure{"Enough Memory?"}
    Pressure -->|No| Full["Full GC Fallback"]

G1GC vs Parallel GC

Feature G1GC Parallel GC
Main goal Balance latency and throughput Maximum throughput
Heap organization Regions Contiguous generations
Old-generation work Mostly concurrent marking plus mixed evacuation Stop-The-World collection
Compaction Incremental through evacuation During full collection
Pause prediction Yes Limited throughput-focused ergonomics
Large heap support Strong Possible, but pauses can become long
Common workload Server and enterprise applications Batch and throughput workloads

G1GC vs ZGC

Feature G1GC ZGC
Primary goal Balanced latency and throughput Extremely low latency
Evacuation Mostly Stop-The-World Mostly concurrent relocation
Typical pause sensitivity Moderate Very high
Throughput overhead Usually lower May be higher depending on workload
Common usage General-purpose server workloads Large heaps and strict latency requirements
Tuning complexity Moderate Often heap-headroom focused

G1 remains a strong general-purpose choice.

ZGC may be preferred when extremely low pause times are more important than maximum throughput.


Frequently Asked Interview Questions

Question 1: What is G1GC?

Answer

G1GC is a region-based, generational Garbage Collector designed to balance application throughput and predictable pause times.

It divides the Java heap into multiple equal-sized regions and prioritizes regions containing the most reclaimable garbage.


Question 2: Why is it called Garbage-First?

Answer

After concurrent marking, G1 has liveness information for old regions.

It prioritizes regions expected to provide the most reclaimed memory relative to their collection cost.

Therefore, regions containing more garbage are generally considered first.


Question 3: Is G1GC a generational collector?

Answer

Yes.

G1 logically divides objects into young and old generations.

However, instead of using one large contiguous space for each generation, it assigns individual heap regions dynamically as Eden, Survivor, or Old regions.


Question 4: Is G1GC completely concurrent?

Answer

No.

G1 performs several activities concurrently, including much of old-generation marking and remembered-set refinement.

However, important phases remain Stop-The-World, including:

  • Young evacuation
  • Initial Mark
  • Remark
  • Parts of Cleanup
  • Mixed evacuation
  • Full GC fallback

Question 5: What is a G1 region?

Answer

A region is the basic heap-management unit in G1.

A region can dynamically serve as:

  • Eden
  • Survivor
  • Old
  • Humongous
  • Free

The JVM selects the region size based on heap size unless explicitly configured.


Question 6: What is a Collection Set?

Answer

A Collection Set is the group of regions selected for evacuation during one GC pause.

Young collections contain young regions.

Mixed collections contain young regions and selected old regions.


Question 7: What is evacuation in G1?

Answer

Evacuation is the process of copying live objects from selected regions into other regions.

After copying completes, the original regions can be reclaimed entirely.

This provides incremental heap compaction.


Question 8: What is a Young GC in G1?

Answer

A Young GC is a Stop-The-World evacuation pause that collects young regions.

Live objects are copied into:

  • Survivor regions
  • Old regions when promoted

The original young regions are then reclaimed.


Question 9: What is a Mixed GC?

Answer

A Mixed GC collects:

  • Young regions
  • Selected old regions

Mixed collections occur after a concurrent marking cycle and reclaim garbage from the old generation incrementally.


Question 10: What is the difference between Mixed GC and Full GC?

Answer

Mixed GC Full GC
Collects selected young and old regions Processes the entire heap
Incremental Global fallback operation
Uses region evacuation May perform full compaction
Intended normal G1 behavior Usually indicates severe pressure
Normally shorter Usually much more expensive

Question 11: What is concurrent marking?

Answer

Concurrent marking identifies live objects and region liveness while application threads continue running.

The result helps G1 choose old regions for later Mixed collections.


Question 12: What is SATB?

Answer

SATB means Snapshot-At-The-Beginning.

It is the marking approach G1 uses to preserve the logical reachability snapshot from the beginning of a marking cycle while application threads continue changing object references.

Write barriers record relevant overwritten references.


Question 13: What is a Remembered Set?

Answer

A Remembered Set tracks heap locations outside a region that may contain references into that region.

It allows G1 to collect selected regions without scanning the entire heap.


Question 14: What is a Card Table?

Answer

A Card Table divides heap memory into small tracking units called cards.

When an application modifies a reference, G1 marks the corresponding card as dirty.

Refinement processing later uses those dirty cards to maintain cross-region reference information.


Question 15: What is concurrent refinement?

Answer

Concurrent refinement is the background processing of dirty cards.

Refinement threads examine changed heap areas and update remembered sets.

If refinement falls behind, card-processing work can increase application or GC-pause overhead.


Question 16: What is a humongous object?

Answer

An object whose size is at least half of one G1 region is treated as humongous.

It is allocated directly into one or more contiguous humongous regions rather than normal Eden regions.


Question 17: Why are humongous objects problematic?

Answer

They can:

  • Waste unused space in the final region
  • Require contiguous regions
  • Increase fragmentation pressure
  • Trigger earlier marking activity
  • Increase Full GC risk
  • Be expensive to relocate

Applications should avoid unnecessarily large temporary arrays and payloads.


Question 18: Does MaxGCPauseMillis guarantee the pause time?

Answer

No.

It is a soft target.

G1 uses prediction models to attempt to meet the target, but actual pauses can exceed it because of live-data volume, remembered-set processing, CPU pressure, humongous allocations, or evacuation failure.


Question 19: What happens if MaxGCPauseMillis is set too low?

Answer

Possible results include:

  • Smaller young generation
  • More frequent GC pauses
  • Higher GC CPU overhead
  • Reduced throughput
  • Insufficient time to reclaim old regions
  • Greater risk of falling behind the allocation rate

A lower value is not automatically better.


Question 20: What is IHOP?

Answer

IHOP means Initiating Heap Occupancy Percent.

It influences when G1 begins a concurrent marking cycle.

Modern G1 commonly uses adaptive IHOP calculations based on allocation rate and marking-cycle duration.


Question 21: What causes evacuation failure?

Answer

Evacuation failure occurs when G1 cannot find enough free destination regions for surviving objects.

Common causes include:

  • Heap exhaustion
  • High live-data volume
  • Memory leaks
  • Insufficient reserve
  • Sudden allocation spikes
  • Humongous-object pressure

Question 22: What causes frequent Mixed GCs?

Answer

Possible causes include:

  • High old-generation allocation
  • Rapid object promotion
  • Large long-lived object population
  • Small heap size
  • Aggressive mixed-collection settings
  • Insufficient garbage reclaimed per cycle

Frequent Mixed GCs are not automatically a problem. Their duration and effectiveness must also be examined.


Question 23: What causes frequent Full GC in G1?

Answer

Common causes include:

  • Memory leaks
  • Heap configured too small
  • Evacuation failure
  • Concurrent marking completing too late
  • Excessive humongous allocations
  • Explicit GC requests
  • Metaspace or class-unloading pressure
  • Very high live-set size

Frequent Full GC should always be investigated.


Question 24: Is G1GC always better than Parallel GC?

Answer

No.

G1 is generally better when predictable response time matters.

Parallel GC may provide higher throughput for:

  • Offline batch jobs
  • CPU-intensive processing
  • Workloads where long pauses are acceptable

The correct collector depends on measurable application goals.


Question 25: When should you choose G1GC?

Answer

G1 is a strong choice when:

  • The application runs on multiple processors.
  • The heap is medium or large.
  • Both throughput and latency matter.
  • Long Full GC pauses must be reduced.
  • The workload includes a meaningful long-lived object set.
  • You need a general-purpose server collector.

Question 26: Can G1 return unused heap memory to the operating system?

Answer

Yes, under supported conditions G1 can uncommit unused heap memory and return it to the operating system.

JEP 346 enhanced G1 so idle applications can return committed memory more promptly instead of depending mainly on Full GC or allocation-driven concurrent cycles.


Question 27: Can G1 eliminate memory leaks?

Answer

No.

G1 can only reclaim unreachable objects.

If objects remain reachable through:

  • Static collections
  • Unbounded caches
  • ThreadLocal values
  • Active listeners
  • Long-running queues

they remain live from the collector's perspective.


Question 28: How do you enable string deduplication with G1?

Answer

Use:

-XX:+UseStringDeduplication

This feature attempts to reduce memory consumption by allowing duplicate String objects to share backing character or byte data where appropriate.

It introduces processing overhead and should be enabled only after measurement.


Question 29: How do you log G1GC activity?

Answer

Basic logging:

-Xlog:gc

Detailed logging:

-Xlog:gc*=info

More diagnostic detail:

-Xlog:gc*=debug

Write logs to a file:

-Xlog:gc*:file=/logs/gc.log:time,uptime,level,tags

Production logging should include rotation:

-Xlog:gc*:file=/logs/gc.log:time,uptime,level,tags:filecount=10,filesize=20M

Question 30: What G1 metrics should be monitored?

Answer

Important metrics include:

  • GC pause duration
  • Young GC frequency
  • Mixed GC frequency
  • Full GC count
  • Allocation rate
  • Promotion rate
  • Old-generation occupancy
  • Live-set size after GC
  • Humongous-region count
  • Evacuation failure events
  • Concurrent marking duration
  • Remembered-set processing time
  • CPU consumed by GC
  • Heap occupancy after collection

Important G1GC JVM Options

Option Purpose
-XX:+UseG1GC Enables G1
-Xms Initial heap size
-Xmx Maximum heap size
-XX:MaxGCPauseMillis Soft pause-time goal
-XX:G1HeapRegionSize Configures region size
-XX:InitiatingHeapOccupancyPercent Influences marking start
-XX:G1ReservePercent Keeps evacuation reserve
-XX:ConcGCThreads Concurrent GC worker count
-XX:ParallelGCThreads Parallel pause worker count
-XX:+UseStringDeduplication Enables G1 string deduplication
-Xlog:gc* Enables detailed GC logging

Avoid changing many G1 options at once.

Start with:

-Xms4g
-Xmx4g
-XX:+UseG1GC
-XX:MaxGCPauseMillis=200
-Xlog:gc*:file=/logs/gc.log:time,uptime,level,tags

Then test under realistic load.


Example Spring Boot Configuration

java \
  -Xms2g \
  -Xmx2g \
  -XX:+UseG1GC \
  -XX:MaxGCPauseMillis=200 \
  -XX:+HeapDumpOnOutOfMemoryError \
  -XX:HeapDumpPath=/dumps/application.hprof \
  -Xlog:gc*:file=/logs/gc.log:time,uptime,level,tags:filecount=10,filesize=20M \
  -jar employee-service.jar

Using equal Xms and Xmx can reduce heap-resizing variability, but it also keeps that heap capacity committed or reserved according to JVM and operating-system behavior.

In containers, confirm that:

  • The pod memory limit includes heap and non-heap memory.
  • Native memory has sufficient headroom.
  • Metaspace, thread stacks, direct buffers, code cache, and JVM overhead are considered.
  • CPU limits are not throttling GC worker threads.

Real-Time Production Scenario 1: Long Mixed GC Pauses

Problem

A Spring Boot service experiences periodic response-time spikes.

GC logs show:

Pause Young (Mixed)
Pause Young (Mixed)
Pause Young (Mixed)

Each pause takes between 800 milliseconds and 1.5 seconds.

Possible causes

  • Too many old regions selected
  • High live-data percentage in old regions
  • Large remembered sets
  • High cross-region reference volume
  • CPU throttling
  • Very strict container CPU limits
  • High object-copying cost

Investigation

Check:

  • Collection Set size
  • Eden-region count
  • Old-region count
  • Remembered-set processing time
  • Object-copy time
  • Number of GC workers
  • CPU throttling metrics
  • Post-GC old-generation occupancy

Potential actions

  • Increase heap headroom.
  • Review the application live set.
  • Reduce large retained caches.
  • Investigate cross-region object graphs.
  • Adjust the pause target only after measurement.
  • Provide sufficient CPU resources.
  • Avoid blindly increasing ParallelGCThreads.

Real-Time Production Scenario 2: Humongous Allocation Pressure

Problem

GC logs show repeated humongous allocations.

The application processes uploaded files using:

byte[] fileContent = inputStream.readAllBytes();

Some files are hundreds of megabytes.

Root cause

The entire file is loaded into one large byte array.

That byte array occupies multiple humongous regions.

Repeated uploads create:

  • Heap pressure
  • Contiguous-region demand
  • Early marking cycles
  • Possible Full GC
  • Possible OutOfMemoryError

Better approach

Process the file as a stream:

try (InputStream input = file.getInputStream();
     BufferedInputStream buffered = new BufferedInputStream(input)) {

    byte[] buffer = new byte[8192];
    int bytesRead;

    while ((bytesRead = buffered.read(buffer)) != -1) {
        processChunk(buffer, bytesRead);
    }
}

This avoids loading the complete file into one large array.


Real-Time Production Scenario 3: Evacuation Failure

Symptoms

  • Heap remains almost full after GC.
  • G1 reports evacuation failure.
  • Pause durations increase sharply.
  • Full GC follows.
  • Application may throw OutOfMemoryError.

Likely causes

  • Application live set is too close to Xmx.
  • Concurrent marking starts too late.
  • Object survival rate is high.
  • A memory leak exists.
  • The heap lacks evacuation headroom.

Resolution process

  1. Capture and inspect GC logs.
  2. Compare pre-GC and post-GC heap occupancy.
  3. Capture a heap dump.
  4. Identify dominant retained objects.
  5. Check caches, queues, and ThreadLocal values.
  6. Increase heap only if the live set is legitimate.
  7. Review allocation and promotion rates.
  8. Adjust reserve or IHOP only when evidence supports it.

Real-Time Production Scenario 4: G1GC Inside Kubernetes

Pod configuration

resources:
  requests:
    memory: "3Gi"
    cpu: "1"
  limits:
    memory: "3Gi"
    cpu: "1"

JVM configuration:

-Xmx3g

Problem

The container is killed even though the Java heap does not exceed 3 GB.

Explanation

Container memory includes more than the Java heap:

Total Container Memory
    |
    +-- Java Heap
    +-- Metaspace
    +-- Thread Stacks
    +-- Direct Buffers
    +-- Code Cache
    +-- GC Structures
    +-- Native Libraries
    +-- JVM Internal Memory

Setting Xmx equal to the entire container limit leaves no room for non-heap and native memory.

Better starting point

For a 3 GiB container, use a lower heap value based on measurements, for example:

-Xms2g
-Xmx2g

The exact value depends on:

  • Thread count
  • Direct-memory usage
  • Metaspace
  • Native libraries
  • Workload
  • Framework behavior

Real-Time Interview Scenario

Question

A Java 21 Spring Boot application uses an 8 GB heap.

The application has:

  • Frequent Young GCs
  • Rare Mixed GCs
  • No Full GCs
  • Young pauses under 100 milliseconds
  • Stable post-GC old-generation occupancy
  • Acceptable application latency

Should you tune G1GC?

Answer

Not necessarily.

Frequent Young GCs alone do not indicate a problem.

If:

  • Pause times meet the service-level objective
  • Throughput is acceptable
  • Old-generation occupancy is stable
  • Full GC is absent
  • CPU overhead is reasonable

then the collector may already be behaving correctly.

GC tuning should solve a measured problem, not merely reduce the number of log entries.


G1GC Troubleshooting Flow

flowchart TD
    Problem["Latency or Memory Problem"] --> Logs["Analyze GC Logs"]
    Logs --> Full{"Frequent Full GC?"}
    Full -->|Yes| Leak["Check Live Set and Memory Leaks"]
    Full -->|No| Pause{"Long Young or Mixed Pauses?"}
    Pause -->|Yes| Breakdown["Inspect Pause-Phase Breakdown"]
    Pause -->|No| CPU{"High GC CPU?"}
    CPU -->|Yes| Allocation["Analyze Allocation Rate"]
    CPU -->|No| Healthy["GC May Be Healthy"]
    Leak --> Dump["Capture Heap Dump"]
    Breakdown --> RSet["Inspect RSet, Copy and CSet Costs"]
    Allocation --> Profile["Use JFR or Allocation Profiling"]

Best Practices

  • Start with JVM defaults whenever possible.
  • Set a realistic heap size.
  • Treat MaxGCPauseMillis as a goal, not a guarantee.
  • Enable GC logs in production.
  • Monitor post-GC occupancy, not only pre-GC usage.
  • Investigate frequent Full GC immediately.
  • Stream large files instead of loading them into huge arrays.
  • Use bounded caches.
  • Avoid unnecessary large object graphs.
  • Provide sufficient container CPU and memory headroom.
  • Change one tuning option at a time.
  • Benchmark with production-like traffic.
  • Fix application memory leaks before tuning the collector.

Common Mistakes

❌ Assuming G1 has no Stop-The-World pauses.

❌ Setting an extremely low pause target without testing.

❌ Setting Xmx equal to the complete container limit.

❌ Manually changing the region size without evidence.

❌ Increasing heap size to hide a memory leak.

❌ Ignoring humongous-object allocations.

❌ Treating every Young GC as a performance problem.

❌ Changing multiple GC flags simultaneously.

❌ Assuming G1 always outperforms Parallel GC.

❌ Using System.gc() to manage production memory.


Senior Interview Tips

Senior interviewers may ask you to explain:

  • Why G1 uses regions
  • How evacuation provides compaction
  • Difference between Young and Mixed GC
  • How SATB supports concurrent marking
  • Why Remembered Sets are required
  • Relationship between Card Tables and Remembered Sets
  • How G1 predicts pause times
  • Why humongous objects create pressure
  • Causes of evacuation failure
  • Why Full GC still occurs
  • How container CPU limits affect G1
  • How you would investigate a long Mixed GC pause
  • When Parallel GC or ZGC may be better

A strong answer should not only define G1 internals. It should connect them to observable production behavior.


Quick Revision Table

Concept Interview answer
Region Basic G1 heap-management unit
Collection Set Regions selected for one GC pause
Young GC Evacuates young regions
Concurrent Mark Determines heap liveness concurrently
Mixed GC Collects young plus selected old regions
SATB Concurrent-marking snapshot strategy
Remembered Set Tracks external references into a region
Card Table Tracks modified heap areas
Evacuation Copies live objects and frees source regions
Humongous Object Object at least half a region in size
IHOP Influences concurrent-marking start
Full GC Expensive fallback when normal G1 work fails
Pause Goal Soft target used for collection-set sizing

Summary

In this article, we covered:

  • G1GC architecture
  • Region-based heap organization
  • Eden, Survivor, Old, and Humongous regions
  • Young-only collection
  • Concurrent marking
  • Initial Mark, Remark, and Cleanup
  • Mixed Garbage Collection
  • SATB
  • Remembered Sets
  • Card Tables
  • Concurrent refinement
  • Evacuation and compaction
  • Humongous-object allocation
  • Pause-time prediction
  • IHOP
  • Evacuation failure
  • Full GC fallback
  • Important JVM options
  • Kubernetes considerations
  • Production troubleshooting
  • Real interview questions
  • Best practices and common mistakes

In the next article, we will explore ZGC, including:

  • Low-latency Garbage Collection
  • Concurrent marking
  • Concurrent relocation
  • Load barriers
  • Colored pointers
  • Generational ZGC
  • Heap headroom
  • ZGC tuning
  • G1GC vs ZGC
  • Production troubleshooting