skip to content

What is an OutOfMemoryError in Java, why is it an Error rather than an Exception, and should you catch it?

level: juniorimportance: must knowfreq 70%

answer

  1. Allocation failed AND GC couldn't reclaim enough
  2. Error branch = normally unrecoverable
  3. Catchable but usually wrong to keep running
  4. Catch only to log/shutdown or for a known bounded alloc
  5. Read the variant message first

basics

~20 s

OutOfMemoryError means the JVM ran out of memory and could not get more, so it can't keep running normally. It's an Error, not a regular Exception, because it signals a serious problem you usually can't recover from. Don't catch it to keep going; fix the root cause instead.

solid answer

~50 s

OutOfMemoryError (OOME) is thrown when the JVM cannot allocate memory and the garbage collector can't reclaim enough to satisfy the request. It lives under java.lang.Error, the branch reserved for serious problems an application normally shouldn't try to recover from, as opposed to Exception. Technically it's a Throwable, so you can catch it, but in general you shouldn't: after an OOME the JVM may be in a fragile state, threads may have died mid-task, and the underlying shortage usually persists, so catching just hides the symptom. The legitimate uses are narrow: logging or triggering a graceful shutdown, or recovering when you know exactly which bounded allocation failed (e.g. a deliberately oversized buffer). The real fix is reading the message to find the variant (heap, Metaspace, GC overhead), then diagnosing a leak or under-sizing. Note OOME can be thrown from any allocation site, not just where the leak lives.

code

java · 13 lines
java
// OOME is a Throwable, so this compiles and catches it —
// but catching to 'keep going' is almost always wrong.
try {
    int[] huge = new int[Integer.MAX_VALUE]; // requests ~8 GB
    use(huge);
} catch (OutOfMemoryError e) {
    // Acceptable: this was ONE bounded allocation we can fall back from.
    log.error("Allocation too large, using streaming fallback", e);
    processInChunks();
}

// NOT acceptable: a blanket catch that swallows OOME and loops on,
// hoping the next iteration works. The shortage is still there.

go deeper

for a junior

Knows OOME means 'out of memory', that it's an Error (serious, not a normal exception), and that you don't just catch it to keep going.

for a middle

Explains it's thrown only after GC can't reclaim enough; knows the catchable-but-usually-wrong nuance and the narrow legitimate uses (log/shutdown, bounded alloc).

for a senior

Frames it within the Throwable hierarchy, articulates why continuing is unsafe (fragile JVM state, persistent shortage), and pivots immediately to reading the variant and diagnosing leak vs. sizing.

for a principal

Sets organizational policy: fail-fast-and-restart on OOME with heap dumps captured, supervised processes, and why a swallow-and-continue posture is a reliability anti-pattern in production fleets.

## What memory the JVM manages A running Java program stores objects in regions managed by the JVM. The two you must know: the **heap** (where every object you create with `new` lives) and **Metaspace** (where the JVM stores class metadata — the definition of each loaded class). A background process called the **garbage collector (GC)** automatically frees heap objects that are no longer **reachable** (nothing in the running program can still refer to them). ## What OutOfMemoryError is `java.lang.OutOfMemoryError` is thrown when the JVM tries to allocate memory for something (a new object, a new class, a thread stack, etc.) and **cannot** — and the GC has already run and still cannot reclaim enough free space to satisfy the request. In short: demand exceeds available memory and there's nothing left to clean up. ## Why it's an `Error`, not an `Exception` Java's throwable hierarchy is: ``` Throwable ├── Error (serious problems, normally unrecoverable) │ └── OutOfMemoryError │ └── StackOverflowError ... └── Exception (conditions a program may reasonably handle) └── RuntimeException ... └── IOException ... ``` By convention, **`Error` is for conditions the application is not expected to catch or recover from** — they indicate the runtime itself is in trouble. `OutOfMemoryError` sits here because once you're out of memory, normal operation can't be assumed to continue: the very next allocation (even building an error message) might fail too. ## Can you catch it? Should you? `OutOfMemoryError` extends `Throwable`, so a `catch (OutOfMemoryError e)` or `catch (Throwable t)` **will** catch it — Java doesn't forbid it. But you usually **shouldn't try to keep running**, because: 1. The shortage is typically **persistent** — the leak or under-sizing is still there, so you'll just hit it again. 2. The JVM may already be **partially broken**: an OOME can fire in the middle of any thread's work, leaving objects half-initialized or locks held. 3. Even your recovery code allocates memory, which may itself fail. Legitimate, narrow uses of catching it: - **Fail fast and clean**: log the error and trigger a controlled shutdown so an orchestrator can restart the process. - **Bounded local recovery**: when you deliberately attempted one large, isolated allocation (e.g. `new byte[hugeSize]`) and can fall back to a smaller path. Here you know exactly what failed. ## The right response The message after `OutOfMemoryError:` tells you the **variant** (e.g. `Java heap space`, `Metaspace`, `GC overhead limit exceeded`) — that's your first diagnostic. From there you decide whether it's a genuine **leak** (memory that should have been freed but is still reachable), an **under-sized** region (workload legitimately needs more than the configured limit), or a **runaway allocation** (one request asking for an absurd amount). ## Key takeaways - OOME = allocation failed *and* GC couldn't help. - It's an `Error` because it's normally unrecoverable. - Catchable, but catching to continue is almost always wrong; catch only to log/shut down or to recover a known bounded allocation. - The fix is diagnosis (read the variant) + addressing leak vs. sizing.

  • If catching OutOfMemoryError is usually wrong, when is logging-then-rethrow or shutdown justified?
    When you want a controlled exit: log the OOME (and ideally dump the heap) so the cause is captured, then let the process die so a supervisor/orchestrator restarts it fresh, rather than limping along in a corrupted state.
  • Does the stack trace of an OOME point to the code that leaks memory?
    Not necessarily. OOME is thrown at whatever allocation happens to push memory over the edge — that's often innocent code, not the leak. You need a heap dump and retained-size analysis to find the actual culprit.

It's like a kitchen running out of counter space mid-service: you can technically notice and announce it, but you can't keep cooking as if nothing happened — you stop, clear the real cause, and restart cleanly.

saying these in an interview costs you the question

  • Claiming you cannot catch an OutOfMemoryError at all (you can; it's a Throwable — the point is you usually shouldn't continue).
  • Treating OOME like a normal recoverable exception and wrapping work in catch-and-retry loops.
  • Assuming the OOME stack trace marks the leaking code.
  • Confusing OutOfMemoryError with StackOverflowError (that's thread-stack exhaustion, a different problem).

context