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:
- Allocate the object in Eden.
- Perform a Young GC when Eden fills.
- Copy live objects into Survivor or Old regions.
- Reclaim the original Eden regions.
- 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:
- Young-only collection
- Concurrent marking cycle
- Mixed collection
- 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
- Capture and inspect GC logs.
- Compare pre-GC and post-GC heap occupancy.
- Capture a heap dump.
- Identify dominant retained objects.
- Check caches, queues, and ThreadLocal values.
- Increase heap only if the live set is legitimate.
- Review allocation and promotion rates.
- 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
MaxGCPauseMillisas 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