RadhaPublication
HomeInterviewLearning PathsRole TracksMockBooksPricing
Login / Sign UpLog in

© 2026 Radha Publication. All rights reserved.

Shop on AmazonDonateContactPrivacyTerms
Java questions
EasyJava#98 min readJul 11, 2026

What is OutOfMemoryError?

practice
learning
Practice modeTest yourself instead of reading straight through

Think of the JVM like a backpack: when it is packed too full, the next object simply cannot fit, no matter how hard you push.

Question: What is OutOfMemoryError?

Answer: OutOfMemoryError is a Java Error that the JVM throws when it cannot find enough memory to create or keep an object, array, class metadata, thread stack, or other internal data. It usually means the runtime is out of a specific memory area, not just that “the computer has no RAM left.” In practice, it often points to a memory leak, an oversized workload, or a JVM memory setting that is too small.

Interview-Ready Answer: In Java, OutOfMemoryError means the JVM could not allocate memory where it needed it, so object creation or internal work failed. It is an Error, not a normal application Exception, which tells me the problem is usually serious and often not safely recoverable. For example, the heap may be full, but the same error can also come from metaspace, direct buffers, or native thread stacks. My first instinct in production would be to look at GC logs, heap dumps, and JVM memory settings like -Xmx.

🧠 Memory Map
Memory map — visual summary of this topic

What it is

OutOfMemoryError is a class in java.lang that extends Error. A quick definition: an Error is a serious JVM-level problem that application code usually should not treat like a normal business failure. The JVM throws this when it cannot satisfy a memory request after trying the normal recovery steps.

How it works under the hood

  1. Your code asks for memory: for example, new, an array, a string expansion, a direct buffer, a new class, or a new thread.
  2. The JVM checks the relevant memory area. For ordinary objects, that is usually the heap; for class metadata, it is metaspace; for NIO direct buffers, it is native memory; for threads, it is native stack memory.
  3. If the request is for heap space, the garbage collector may run first. Garbage collection means the JVM tries to reclaim objects that are no longer reachable, meaning nothing in the program can still use them.
  4. If GC frees enough space, allocation succeeds. If not, the JVM throws an OutOfMemoryError with a message that hints at the failing area.
  5. If the request is for a non-heap resource, the JVM may fail immediately because it cannot reserve more native memory from the operating system.
  6. If the error is uncaught on an important thread, the request fails and the process may terminate or become unstable.

Common messages and what they mean

MessageAreaMeaning
Java heap spaceHeapNot enough room for objects or arrays
MetaspaceClass metadataToo many classes or class loaders
GC overhead limit exceededHeap + GCGC is working too hard and reclaiming too little
Direct buffer memoryNative direct memoryNIO direct buffers hit their limit
unable to create new native threadOS/native stacksThe JVM cannot reserve another thread stack

Why interviewers care

This question checks whether you know that “memory” in Java is not one single bucket. A common mistake is to say “just increase the heap.” That may help for Java heap space, but it does nothing for metaspace leaks, direct buffer leaks, or too many threads.

Performance and practical numbers

Allocation checks are usually fast, but the failure path can be expensive because GC may run multiple times before the JVM gives up. On large heaps, a full GC can pause for milliseconds to seconds depending on heap size, object count, and CPU. There is no fixed “big-O” answer for OOME itself; the real cost comes from how much memory must be scanned and reclaimed.

Some useful realities: thread stacks are often around 1 MB each on many 64-bit HotSpot builds, but the exact size is platform- and flag-dependent; metaspace has no fixed default cap unless you set -XX:MaxMetaspaceSize; and the heap maximum comes from -Xmx or container-aware JVM ergonomics.

Comparison with related ideas

TermWhat it isKey difference
Memory leakBugObjects stay reachable, so GC cannot free them
OutOfMemoryErrorFailureThe JVM finally cannot allocate memory
Garbage collectionMechanismNormal cleanup, not an error by itself

Important edge cases

  • You can technically catch OutOfMemoryError, but the JVM may already be in a stressed state, so recovery is often unreliable.
  • Not all OOMEs are heap-related; direct memory and native threads fail outside the heap.
  • GC overhead limit exceeded is a special OOME path where HotSpot decides GC is making almost no progress; by default it is enabled and uses a heuristic based on spending most time in GC with very little memory recovered.

Real-world story

Imagine a checkout service in an e-commerce app that builds invoice PDFs. A developer adds an in-memory cache for every generated invoice to reduce recomputation, but never sets a size limit. During a big sale, the service keeps retaining old byte arrays and request objects, the heap grows, full GCs start happening every few seconds, and eventually the pod crashes with java.lang.OutOfMemoryError: Java heap space.

What goes wrong: latency climbs, requests time out, logs show repeated long GC pauses, and the pod may restart repeatedly. Users see failed checkouts or missing invoices, while the root cause is not “Java is slow” but “objects were kept alive too long.”

The fix is usually to cap the cache, release references, reduce retained data, or move large blobs out of the heap. In production, the diagnosis often starts with GC logs, heap dumps, and a quick check of whether the growth is in heap, metaspace, direct buffers, or threads.

Java
import java.util.ArrayList;
import java.util.List;

public class Main {
    // A tiny memory budget simulator.
    // This is safe to run: it demonstrates the meaning of OutOfMemoryError
    // without trying to exhaust your real JVM heap.
    static class ToyHeap {
        private final int capacityBytes;
        private int usedBytes;
        private final List<byte[]> retained = new ArrayList<>();

        ToyHeap(int capacityBytes) {
            this.capacityBytes = capacityBytes;
        }

        void allocate(int bytes) {
            if (bytes <= 0) {
                throw new IllegalArgumentException("bytes must be positive");
            }

            // The important idea: if memory is already reserved/retained,
            // there may be no room left for the next allocation.
            if (usedBytes + bytes > capacityBytes) {
                throw new OutOfMemoryError(
                        "ToyHeap exhausted: requested " + bytes + " bytes, used " + usedBytes + " of " + capacityBytes);
            }

            retained.add(new byte[bytes]);
            usedBytes += bytes;
        }

        void releaseAll() {
            // Releasing references matters because only unreachable objects can be reclaimed.
            retained.clear();
            usedBytes = 0;
        }

        int usedBytes() {
            return usedBytes;
        }
    }

    public static void main(String[] args) {
        ToyHeap heap = new ToyHeap(1024 * 1024); // 1 MiB budget
        int[] chunks = {256 * 1024, 400 * 1024, 500 * 1024};

        System.out.println("Allocating chunks into a fixed-size memory budget...");
        try {
            for (int chunk : chunks) {
                heap.allocate(chunk);
                System.out.println("Allocated " + chunk + " bytes; used=" + heap.usedBytes());
            }
        } catch (OutOfMemoryError e) {
            System.out.println("Caught OOME: " + e.getMessage());
        }

        System.out.println("Releasing references and trying again...");
        heap.releaseAll();
        System.out.println("After release, used=" + heap.usedBytes());

        try {
            heap.allocate(700 * 1024);
            System.out.println("Allocated again after release; used=" + heap.usedBytes());
        } catch (OutOfMemoryError e) {
            System.out.println("Unexpected OOME after release: " + e.getMessage());
        }
    }
}

Follow-up & Tricky Questions:

  • What is the difference between a memory leak and OutOfMemoryError? A memory leak is the bug; OutOfMemoryError is one possible result. A leak keeps objects reachable so the GC cannot free them, and memory usage grows until allocation fails.
  • Can you catch OutOfMemoryError? Yes, but it is usually a bad recovery strategy. The JVM may already be short on memory, so logging, allocating new objects, or continuing normal work may fail or behave unpredictably.
  • How do you diagnose it in production? Start with GC logs, heap dumps, and JVM flags like -Xmx and -XX:MaxMetaspaceSize. Then check whether the problem is heap growth, class loader retention, direct buffer usage, or too many threads.
  • What are the most common OOME variants? Java heap space, Metaspace, GC overhead limit exceeded, Direct buffer memory, and unable to create new native thread are the ones interviewers most often expect.
  • Does increasing heap always fix it? No. It can hide a leak temporarily, but if the problem is metaspace, direct memory, or runaway thread creation, a bigger heap will not solve it.
  • Why might a small allocation still fail? Because the needed memory area may already be fragmented or reserved for another purpose, or the JVM may be out of native memory even if the Java heap still has room.
  • Is OutOfMemoryError only about the heap? No. The JVM can fail in several memory pools, and each one produces a different clue in the error message.
  • Does nulling a reference free memory immediately? No. It only makes the object eligible for GC. The JVM frees it later, if and when a GC cycle runs and decides the object is truly unreachable.
  • Can a program continue after this error? Sometimes briefly, but you should treat the process as suspect. In real systems, the safest response is often to log, fail fast, and let orchestration restart the service.

Tricky 1: “If I catch OutOfMemoryError, is the app safe again?” Usually no; the JVM may still be unstable, and the root cause is still present.

Tricky 2: “If GC runs, will it always fix the problem?” No; GC only helps if enough garbage is actually unreachable. If live objects are too large, the JVM still fails.

Tricky 3: “Can direct buffers cause heap OOME?” They usually fail with a different message, because direct buffers come from native memory, not the Java heap.

Common Mistakes:

  • Mistake: Thinking OutOfMemoryError always means the heap is full. Correction: It can also come from metaspace, direct memory, or native thread stacks.
  • Mistake: Treating it like a normal recoverable exception. Correction: It is an Error, so the JVM may already be in a damaged state.
  • Mistake: “Fixing” it only by raising -Xmx. Correction: That may hide the symptom, but leaks and unbounded caches still need code fixes.
  • Mistake: Forgetting that retained references prevent GC. Correction: If an object is still reachable, the JVM cannot reclaim it.

Memory Hook: “If the backpack is full, the next book does not fit — and a bigger backpack only helps if the real problem is space, not a hole in the bag or too many bags.”

Cheat Sheet:

  • OutOfMemoryError = JVM could not allocate memory where it needed it.
  • It is an Error, not a normal application exception.
  • Common messages: Java heap space, Metaspace, Direct buffer memory, unable to create new native thread.
  • Typical causes: memory leak, huge cache, too many threads, large direct buffers, class loader leak.
  • Start diagnosis with GC logs, heap dumps, and JVM memory flags.
  • Fix the root cause; do not rely on catching the error and continuing forever.

Practice Tasks:

  • Modify the sample code so the memory budget is 512 KB and observe where the simulated OOME happens.
  • Remove the call to releaseAll() and explain why the second allocation still fails in the model.
  • List three JVM flags or tools you would use to investigate a real production OOME, then match each one to heap, metaspace, or native memory.
Previous
Back to Questions←→to navigate
Next

Recommended Resources

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

Recommended Book

Cracking the Coding Interview

189 Programming Questions and Solutions

import java.util.ArrayList; import java.util.List; public class Main { // A tiny memory budget simulator. // This is safe to run: it demonstrates the meaning of OutOfMemoryError // without trying to exhaust your real JVM heap. static class ToyHeap { private final int capacityBytes; private int usedBytes; private final List<byte[]> retained = new ArrayList<>(); ToyHeap(int capacityBytes) { this.capacityBytes = capacityBytes; } void allocate(int bytes) { if (bytes <= 0) { throw new IllegalArgumentException("bytes must be positive"); } // The important idea: if memory is already reserved/retained, // there may be no room left for the next allocation. if (usedBytes + bytes > capacityBytes) { throw new OutOfMemoryError( "ToyHeap exhausted: requested " + bytes + " bytes, used " + usedBytes + " of " + capacityBytes); } retained.add(new byte[bytes]); usedBytes += bytes; } void releaseAll() { // Releasing references matters because only unreachable objects can be reclaimed. retained.clear(); usedBytes = 0; } int usedBytes() { return usedBytes; } } public static void main(String[] args) { ToyHeap heap = new ToyHeap(1024 * 1024); // 1 MiB budget int[] chunks = {256 * 1024, 400 * 1024, 500 * 1024}; System.out.println("Allocating chunks into a fixed-size memory budget..."); try { for (int chunk : chunks) { heap.allocate(chunk); System.out.println("Allocated " + chunk + " bytes; used=" + heap.usedBytes()); } } catch (OutOfMemoryError e) { System.out.println("Caught OOME: " + e.getMessage()); } System.out.println("Releasing references and trying again..."); heap.releaseAll(); System.out.println("After release, used=" + heap.usedBytes()); try { heap.allocate(700 * 1024); System.out.println("Allocated again after release; used=" + heap.usedBytes()); } catch (OutOfMemoryError e) { System.out.println("Unexpected OOME after release: " + e.getMessage()); } } }