Back to CI/CD Pipeline
Q25Question 25 of 56

The G1 humongous object problem: object larger than 50% of region size: what issues it causes, and how to fix it

Key Concepts
  • Any single object larger than 50% of a G1 region is a humongous object allocated directly into contiguous Old regions.
  • Humongous objects waste the tail of the last region (internal fragmentation) and require contiguous free regions (external fragmentation).
  • A stream of humongous allocations triggers concurrent marking cycles too often and can force a Full GC if no contiguous run of free regions is available.
  • Fix 1: make the objects smaller by streaming or chunking large payloads.
  • Fix 2: increase region size with G1HeapRegionSize so the object drops below the 50% threshold.
  • Fix 3: switch to ZGC, whose concurrent relocation handles large objects gracefully.

Definition

Any object bigger than half a G1 region becomes a humongous object that eats whole regions, wastes the remainder, and adds GC pressure.
  • Humongous object – any single object larger than 50% of a G1 heap region, allocated directly into a contiguous run of Old regions.
  • Internal fragmentation – wasted space in the tail of the last region because nothing else may share humongous regions.
  • External fragmentation – difficulty finding a contiguous run of free regions, which can force a Full GC to compact.
  • G1HeapRegionSize – the JVM flag to set region size (1-32 MB); increasing it raises the humongous threshold.
  • Full GC – the worst-case pause triggered when G1 cannot find contiguous free regions for a humongous allocation.

The warehouse rooms are a fixed size. Most documents fit easily, but occasionally a customer submits one enormous ledger bigger than half a room. The rule says such a giant must get its own locked set of adjacent rooms, even if it only half-fills the last one. If giant ledgers keep arriving, the warehouse fragments into reserved chunks and the cleaning crew has to run extra inspections. That giant ledger is a humongous object.

Understanding the Concepts

What is a humongous object in G1?

G1 region size is a power of two between 1 and 32 MB. The humongous threshold is exactly 50% of the region size. With the common 8 MB region, any object larger than 4 MB is humongous.

BASH
# Check region size and calculate humongous threshold
java -XX:+UseG1GC -Xmx16g -XX:+PrintFlagsFinal -version 2>&1 \
  | grep G1HeapRegionSize
TEXT
uintx G1HeapRegionSize = 8388608   (8 MB)
Humongous threshold = 4 MB (50% of 8 MB)

What causes humongous allocations in banking applications?

A single large byte array, a big cached document, or an oversized serialized message can trip the threshold.

JAVA
package com.bank.service;

import java.math.BigDecimal;

public class StatementGenerator {

    // This creates a humongous object!
    public byte[] generateAnnualStatement(String accountId) {
        // 9 MB array for one customer's annual history
        byte[] statementData = new byte[9 * 1024 * 1024];
        loadTransactionHistory(accountId, statementData);
        return statementData;
    }

    private void loadTransactionHistory(String id, byte[] buf) { }
}

What issues do humongous objects cause?

Humongous objects cause internal fragmentation, external fragmentation, and excessive GC pressure.

DIAGRAM
Region layout with humongous objects (8 MB regions):

  [Eden][Eden][HUMONGOUS-start][HUMONGOUS-cont][Free 1MB wasted!]
  [Old ][Old ][HUMONGOUS-start][HUMONGOUS-cont][Free 3MB wasted!]
  [Free][Free][Free][Free]  ← scattered, not contiguous

Issues:
  1. Internal fragmentation: 9 MB object in 2x8 MB regions = 7 MB wasted
  2. External fragmentation: no contiguous run for next humongous alloc
  3. GC pressure: each alloc may cross IHOP, triggering marking cycles
  4. Full GC: if no contiguous run found → whole-heap compaction

How do you detect humongous allocations?

Enable GC logging and look for humongous allocation entries.

BASH
# Enable humongous allocation logging
java -XX:+UseG1GC \
     -Xlog:gc+humongous=debug:file=gc.log \
     -Xmx16g \
     -jar statement-service.jar

# Search for humongous allocations
grep -i "humongous" gc.log
TEXT
[0.234s] Humongous region allocation request for 9437184 bytes
[0.234s] Allocated humongous region 42 (2 regions for 9437184 bytes)

How do you fix it: make objects smaller?

The best fix is to stream or chunk large payloads so no single allocation crosses the threshold.

JAVA
package com.bank.service;

import java.io.OutputStream;
import java.math.BigDecimal;

public class StatementGeneratorFixed {

    private static final int CHUNK_SIZE = 1024 * 1024; // 1 MB chunks

    // CORRECT: stream in chunks, no humongous allocation
    public void generateAnnualStatement(String accountId,
                                         OutputStream output) {
        byte[] chunk = new byte[CHUNK_SIZE];
        int totalPages = getPageCount(accountId);
        for (int page = 0; page < totalPages; page++) {
            int bytesRead = loadPage(accountId, page, chunk);
            writeToOutput(output, chunk, bytesRead);
        }
    }

    private int getPageCount(String id) { return 10; }
    private int loadPage(String id, int page, byte[] buf) { return buf.length; }
    private void writeToOutput(OutputStream out, byte[] data, int len) { }
}

How do you fix it: increase region size?

If chunking is not feasible, increase the region size so the object drops below the 50% threshold.

BASH
# 16 MB regions → humongous threshold = 8 MB
# A 9 MB object is still humongous!
java -XX:+UseG1GC -XX:G1HeapRegionSize=16m -Xmx16g -jar app.jar

# 32 MB regions → humongous threshold = 16 MB
# A 9 MB object is now NORMAL
java -XX:+UseG1GC -XX:G1HeapRegionSize=32m -Xmx16g -jar app.jar

How do you fix it: switch to ZGC?

If large objects are genuinely intrinsic to the workload, ZGC handles them gracefully because its concurrent relocation and region model do not have the contiguous-run constraint.

BASH
# ZGC: large objects are handled without humongous regions
java -XX:+UseZGC -Xmx16g -jar statement-service.jar

# No humongous fragmentation, no contiguous-run constraint
# Pauses do not grow with object or heap size

What are common mistakes?

The most common mistake is ignoring humongous allocation warnings in the GC log until a Full GC hits production.

BASH
# WRONG: ignore humongous warnings, hit Full GC under load
# [5.678s] GC(12) Pause Full (Allocation Failure) 16G->8G 12.345s

# CORRECT: monitor and fix before production impact
# Alert on: humongous allocation count > threshold
# Alert on: any Full GC event

What should a senior engineer know?

A senior engineer always verifies the fix by enabling the humongous debug log and alerting on any Full GC. At National Bank of India, the end-of-day statement generator was holding each customer's whole annual history in one 9 MB array. Splitting it into 1 MB chunks eliminated every humongous allocation and the occasional Full GC that was freezing the batch.

BASH
# Verify after fix: no more humongous allocations
grep -c "humongous" gc-after-fix.log
TEXT
0

Visual Overview

DIAGRAM
  Normal allocation (< 50% region):
  ┌────────┐
  │  Eden  │ ← object fits in one region
  └────────┘

  Humongous allocation (> 50% region):
  ┌────────┐┌────────┐
  │ HUMONG ││ HUMONG │ ← 9 MB object needs 2 x 8 MB regions
  │ START  ││ CONT   │   7 MB WASTED in second region
  └────────┘└────────┘

  Fix hierarchy:
  ┌─────────────────────────────────────────────┐
  │ 1. Make objects smaller (chunk/stream)       │ ← best fix
  │ 2. Increase G1HeapRegionSize                 │ ← raise threshold
  │ 3. Switch to ZGC (no humongous constraint)   │ ← last resort
  └─────────────────────────────────────────────┘

Key Takeaway

In G1, any single object larger than half a region is humongous. It skips Eden and is placed directly into contiguous Old regions, wasting the tail of the last region and requiring a contiguous run of free regions that may not exist. A stream of humongous allocations triggers concurrent marking cycles too often and can force a Full GC when no contiguous run is available.

The fix order is: make objects smaller by chunking or streaming, increase region size to raise the threshold, or switch to ZGC which handles large objects gracefully. At National Bank of India, splitting the statement generator's 9 MB array into 1 MB chunks eliminated every humongous allocation and the Full GC that was freezing the nightly batch.

Always verify by enabling the humongous debug log and alerting on any Full GC event in production.

Remember one sentence: if an object is bigger than half a G1 region, it is humongous, and the fix is almost always to make the object smaller, not to make the heap bigger.

---

Share
Use ← → arrow keys to navigate