Coding Agents & Terminal · recorded 2026-08-30 · applies to 1 tool(s) ✓ Verified

Problem: Java OOM signature cheat-sheet: heap space / Metaspace / StackOverflowError / native thread / GC Overhead —— Trove problem library

Verified 2026-08-30Java OOM has six distinct signatures — enable HeapDumpOnOutOfMemoryError first, read GC logs to see which resource is exhausted (heap/Metaspace/stack/threads/GC overhead), then tune per signature.

Quick fix

# 1. Enable heap dump first (lands automatically on next OOM), faster than guessing
java -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/tmp/app.hprof -jar app.jar

# 2. GC log shows which area is exhausted
java -Xlog:gc -jar app.jar   # or -verbose:gc

# 3. Common quick wins by signature
# heap space + big file download -> stream or external link, never whole byte[]
# heap space + big query       -> paginate + cache
# thread exhaustion           -> bounded pool; logback discardingThreshold back to default (not 0)
# random OOM                  -> check -Xmn, never equal -Xmx

What you will run into

A Java service suddenly dies / returns 502 / the API answers 500, and the logs show various OOM errors. Different signatures point to completely different root causes - match the signature first:

Error signatureTypical root causeWhere to look
OutOfMemoryError: Java heap spaceHeap filled by big objects/collections: one-shot read of a 200MB file into byte[], an unpaginated 100k-row query, in-memory ZIP-bomb unzip, or -Xmn equal to -Xmxheap dump + MAT, find who owns the memory; check big files / big queries / unzip
OutOfMemoryError: MetaspaceAll XSDs/classes loaded once at startup (JAXB newSchema full init) fills Metaspace with class metadatacheck startup schema/class count; resize or chunked loading
StackOverflowErrorrecursion without an exit: trusting a third-party parameter (they sent -1)parameter validation + recursion to loop
OutOfMemoryError: unable to create new native threadthread count over the OS limit: unbounded pool + blocked logging (disk full + logback AsyncAppender queue full)jstack thread count/stacks; disk, logging config, pool caps
OutOfMemoryError: GC Overhead limit exceededGC churns with zero reclaim: memory leak (e.g. ThreadLocal not cleared), Full GC can't free anythingMAT Incoming references (e.g. ThreadLocalMap)
Random OOM at different placesJVM flag imbalance: -Xmn (young gen) equals -Xmxcheck startup script -Xmn/-Xmx ratio

Why this happens

JVM memory has four independent areas - heap / metaspace / thread stacks / native threads - each can be exhausted on its own:

AreaExhaustion mechanism
Heapobjects keep accumulating: whole-file reads, unpaginated big queries, ZIP bombs (tiny archive inflates to 1000x+)
Metaspaceclass metadata: one-shot newSchema of all XSDs loads many classes
Thread stacksdeep recursion: StackOverflowError
Native threadsOS thread limit (ulimit -u / /proc/sys/kernel/threads-max): unbounded pool + business threads blocked on logging (disk full + AsyncAppender queue full with discardingThreshold=0)
GC churnleaked objects cannot be freed: Full GC reclaims <2% while taking >98% of the time -> GC Overhead limit

JVM "memory" is not one thing — it is four independent exhaustion channels, each with different OOM text and different fixes: the Java heap is GC-managed and only exhausts when objects accumulate;

Metaspace stores class metadata and exhausts when classes are generated dynamically (e.g.

full XSD→classes); thread stacks are fixed per thread (~1MB class) and exhaust via deep recursion (StackOverflowError);

native threads are bounded by the OS thread limit (ulimit -u / threads-max) and exhaust when an unbounded pool only grows (threads blocked on log writes).

GC thrashing (reclaiming <2% while using >98% of time) is the fingerprint of "many unreachable-but-uncollected objects".

Deterministic takeaway: the region name in the OOM text (Java heap space / Metaspace / unable to create native thread / GC overhead) decides the direction — heap OOM: object accumulation and leaks;

Metaspace OOM: class-loading volume; native-thread OOM: thread count and blocking;

do not blanket-apply "-Xmx"; fixed diagnostic: read the exception class name before acting.

Step by step

1. Record the signature (decide the class first) — Required

Java heap space / Metaspace / StackOverflowError / unable to create new native thread / GC Overhead limit exceeded / Direct buffer memory - different signature, different weapon.

2. Reproduce and collect evidence — Required

  • Enable -XX:+HeapDumpOnOutOfMemoryError to get a .hprof on OOM;
  • jmap -dump:format=b,file=app.hprof <pid> while the service is still alive;
  • Open the dump in MAT: Dominator Tree big objects + Incoming references (case 8: ThreadLocal leak = Incoming references point to ThreadLocalMap);
  • jstack <pid> for thread stacks (check total threads when native threads are exhausted).

3. Fix by root cause — Required

Root causeFix
whole-file read into memory (readFileContent full byte[])stream it (buffered FileInputStream->OutputStream) or move the file to FTP/object storage as an external link
unpaginated big query (100k+ rows repeatedly)row limit + query cache + longer polling interval
ZIP bomb (tiny archive, huge inflation)forbid nested archives, validate file size and paths, extract to disk with a total cap
Metaspace (all XSDs loaded once)temporary -XX:MaxMetaspaceSize bump; real fix: service split / load schemas on demand
recursion without exit (third party sent -1)strict parameter validation + recursion to loop
unbounded pool + blocked loggingbounded pool; AsyncAppender discardingThreshold back to a sane value (0 = never drop, but also no backpressure protection; disk full stalls threads)
-Xmn = -Xmxrestore a sane ratio (young gen about 1/3~1/2 of the heap, tune by workload)
ThreadLocal leakremove in finally; pooled threads otherwise pin references forever

4. Verify — Required

Re-run the original trigger path under load: no OOM, stable GC log, thread count back to normal.

How to confirm it works

The same trigger path no longer OOMs, the GC log shows no consecutive zero-reclaim Full GCs, the thread count is stable, and the old big objects / leak references are gone from a fresh dump.

Other environments

EnvironmentNotes
modern JVM (G1/ParallelGC)with a tiny heap and fast allocation the GC Overhead signature may render as plain Java heap space (verified locally); do not only grep for the literal signature
Not verifiedMetaspace / native thread / unbounded pool + logback blocking scenarios (no matching production environment); mechanisms and fixes come from source cases 2/4

Symptom

Java service OOMs: Java heap space (big file / big query / ZIP bomb / -Xmn imbalance), Metaspace (full schema load), StackOverflowError (recursion without exit), unable to create new native thread (thread exhaustion), GC Overhead limit exceeded (GC churn).

Cause

Each JVM memory area exhausts independently: heap filled by big objects/collections, Metaspace by class metadata, thread stacks by deep recursion, native threads over the OS limit, and leaked objects make GC churn with zero reclaim.

Solution

  1. Enable -XX:+HeapDumpOnOutOfMemoryError + GC log first;
  2. Match the error signature to its class (see the cheat-sheet);
  3. Stream big files / external links, paginate + cache big queries, block ZIP bombs (size/nesting/path checks), chunked schema loading, parameter validation + recursion to loop, bounded thread pool + non-blocking logging, sane -Xmn ratio, remove ThreadLocal in finally;
  4. Prove with MAT/jstack, then verify under load.
Verification Evidence
  • Mechanism repro (2026-08-30, WSL Ubuntu, OpenJDK 17.0.18): 1) -Xmx64m unbounded allocation -> OutOfMemoryError: Java heap space; 2) infinite recursion -> StackOverflowError (depth 22691); 3) ZIP bomb (200MB of repeated chars compressed to 204KB, ratio >1000x) inflated in memory -> Java heap space (case-1 mechanism); 4) -Xmx16m slow leak -> 31 Full GCs in 16s with 14M->14M zero reclaim (GC-churn mechanism verified; literal GC Overhead signature not stably reproduced). local repro log 2026-08-30.
  • Not verified: Metaspace / native thread / unbounded pool + logback blocking (no matching environment); mechanisms and fixes from source cases 2/4.

Applicable tools (1)

This problem is linked from the tool pages below — reachable from either side.

jdk

Source & evidence

Recorded Verified ✓ VerifiedApplies to 1 tool(s)
Entries contain locally measured or reviewable-source solutions; unmeasured items are explicitly labeled.

Source: https://www.cnblogs.com/mylibs/p/19321491/production-accident-vl01

← Coding Agents & Terminal domain | Problem Catalog Experience | Facing another issue? Go to the Helpdesk