Of course. A java.lang.OutOfMemoryError is one of the most common and critical runtime errors in Java applications. It's not an exception that you can typically catch and handle gracefully; it's a fatal error that signals the Java Virtual Machine (JVM) has exhausted its available memory and cannot allocate any more.

Let's break down what it is, why it happens, and how to diagnose and fix it.
What is OutOfMemoryError?
In simple terms, your Java application is trying to use more memory than the JVM has been configured to use. The JVM manages a finite amount of memory, and when it needs to allocate new objects but can't find a large enough contiguous block of free memory, it throws this error.
It's crucial to understand that this is an Error, not an Exception. Errors represent serious problems that a reasonable application should not try to catch. They indicate a fundamental problem with the JVM or the environment, not a flaw in your application's logic.
The Two Main Types of Memory in the JVM
To understand the cause, you need to know the two main memory areas where OutOfMemoryError occurs:

a) Heap Space (Most Common)
This is where your application's live objects are stored. When you create a new object (e.g., new String("hello")), memory is allocated on the heap.
- How it works: The Garbage Collector (GC) automatically reclaims memory from objects that are no longer being used (i.e., there are no more references to them).
OutOfMemoryError: Java heap spaceoccurs when:- Memory Leak: Your application has a bug where objects are continuously created but the references to them are never released, preventing the GC from cleaning them up. Over time, these leaked objects fill up the entire heap.
- Insufficient Heap Size: Your application is working correctly, but it genuinely needs more memory than the JVM's maximum heap size (
-Xmx) allows.
b) PermGen / Metaspace (For Class Metadata)
This area stores metadata about classes and methods (e.g., class names, method names, field information, bytecodes, etc.).
-
Important Change:
- Before Java 8: This area was called PermGen (Permanent Generation), and its size was fixed (by
-XX:MaxPermSize). This was a common source ofOutOfMemoryErrorwhen loading many classes (e.g., in web containers with many web applications or heavy use of reflection/bytecode manipulation libraries like CGLIB). - Java 8 and later: PermGen was replaced by Metaspace. Metaspace is allocated from the native OS memory and can grow dynamically (up to a limit set by
-XX:MaxMetaspaceSize). This makes it much less likely to run out of space, but it can still happen if you load an excessive number of classes.
- Before Java 8: This area was called PermGen (Permanent Generation), and its size was fixed (by
-
OutOfMemoryError: Metaspace(orPermGenin older versions) occurs when the JVM's metadata storage is exhausted.
(图片来源网络,侵删)
How to Diagnose the Cause
Diagnosis is the most critical step. You need to figure out what is filling up the memory.
Step 1: Get a Heap Dump (The Most Important Step)
A heap dump is a snapshot of the memory at a specific point in time. It shows you exactly which objects are in memory, how many of each there are, and what is keeping them alive.
How to get a heap dump:
-
Method 1: Programmatically (Best for production) Add this code to your application. When you want to trigger a dump, call the
dumpHeapmethod (e.g., via a JMX operation or a web endpoint).import sun.misc.Unsafe; import java.lang.reflect.Field; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; public class HeapDumper { public static void dumpHeap(String filePath, boolean live) { try { Unsafe unsafe = getUnsafe(); Field field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); unsafe = (Unsafe) field.get(null); // Use HotSpotDiagnosticMXBean to get the heap dump // This is a more standard way than using sun.misc.Unsafe com.sun.management.HotSpotDiagnosticMXBean diagnosticBean = com.sun.management.ManagementFactory.newPlatformMXBeanProxy( ManagementFactory.getPlatformMBeanServer(), "com.sun.management:type=HotSpotDiagnostic", com.sun.management.HotSpotDiagnosticMXBean.class); diagnosticBean.dumpHeap(filePath, live); System.out.println("Heap dump saved to " + filePath); } catch (Exception e) { e.printStackTrace(); } } // This is a fallback, using sun.misc.Unsafe is not recommended for production private static Unsafe getUnsafe() throws Exception { Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); return (Unsafe) theUnsafe.get(null); } } -
Method 2: On-the-fly using jcmd (JDK 7+) If the JVM is running, you can use
jcmdfrom the command line.- Find the process ID (PID) of your Java application:
jps -l - Trigger the heap dump:
jcmd <PID> GC.heap_dump /path/to/heapdump.hprof
- Find the process ID (PID) of your Java application:
-
Method 3: On JVM Crash You can configure the JVM to automatically generate a heap dump when it crashes with an
OutOfMemoryError.Add these JVM arguments:
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/path/to/heapdump.hprof
Step 2: Analyze the Heap Dump
Use a specialized tool to inspect the .hprof file. The best tool for this is Eclipse MAT (Memory Analyzer Tool).
- Open the
.hproffile in MAT. - Leak Suspects Report: MAT will automatically run an analysis and give you a "Leak Suspects" report. This is a great starting point and often points directly to the problem.
- Dominator Tree: This is the most powerful view. It shows you which objects are "dominating" large chunks of memory. An object dominates all other objects that it can reach. This helps you find the root of the memory retention.
- Histogram: Shows you a list of all classes and the number of instances and total size of each. You can sort by "Retained Heap" to see which objects are taking up the most memory.
By analyzing the heap dump, you can answer the key questions:
- What kind of objects are filling the heap? (e.g.,
char[],byte[],MyPojo,java.lang.String) - Who is holding a reference to these objects? This is the root cause.
How to Fix the Problem
The solution depends entirely on the diagnosis from the heap dump.
If it's a Heap Space Issue:
-
Fix the Memory Leak (Best Solution):
- If you find a cache that is growing indefinitely, implement a proper eviction policy (e.g., LRU).
- If you find collections that are never cleared, refactor your code to remove references when they are no longer needed.
- Be careful with static collections (
static Map). They live for the entire lifetime of the application and will never be garbage collected unless you explicitly clear them.
-
Increase the Heap Size (Quick Fix / Temporary Solution): If it's not a leak but your application genuinely needs more memory, increase the JVM's maximum heap size.
-Xmx4g: Set the maximum heap size to 4 gigabytes.- Caution: This is often just kicking the can down the road. A memory leak will eventually fill up even a larger heap. Also, ensure your machine has enough physical RAM.
-
Optimize Object Creation:
- Use object pooling for expensive-to-create objects (use with caution, as pools can also cause leaks if not managed properly).
- Be mindful of large data structures. Can you process data in chunks instead of loading everything into memory at once?
If it's a Metaspace Issue:
- Check for Class Loading Issues:
- Are you using reflection or libraries that generate classes at runtime (e.g., Hibernate, CGLIB, Groovy)? These can load a huge number of classes.
- Check for web application classloader leaks in application servers (like Tomcat or JBoss) where classes aren't being unloaded when a web app is redeployed.
- Increase Metaspace Size:
-XX:MaxMetaspaceSize=512m: Set the maximum metaspace size to 512 megabytes. The default is usually unlimited, but setting a limit can help prevent the OS from running out of memory.
General Best Practices:
- Profile Early and Often: Use profiling tools (like VisualVM, YourKit, JProfiler) during development and testing to monitor memory usage and catch leaks before they reach production.
- Use Generational GC: The default Garbage Collector in modern Java versions is very good. For very large heaps (tens of gigabytes), you might consider the G1 Garbage Collector (
-XX:+UseG1GC). - Set JVM Flags for Production:
-Xms2g -Xmx2g # Set initial and max heap to the same size to prevent resizing pauses -XX:+UseG1GC # Use the G1 Garbage Collector -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/var/log/myapp/heapdump.hprof
Example Scenario
Problem: A web application crashes with OutOfMemoryError: Java heap space every night.
Diagnosis:
- The JVM is configured with
-Xmx512m. - A heap dump is captured using
-XX:+HeapDumpOnOutOfMemoryError. - The heap dump is analyzed with Eclipse MAT.
- The Dominator Tree shows that a single
java.util.HashMapinstance is retaining over 400MB of memory. - The Path to GC Roots for this map reveals that it's a static cache:
com.myapp.Cache.getInstance().data.
Root Cause: The Cache class has a static Map that is populated with user session data every day. The cache's clear() method is never called, so the data from the previous day remains in memory, accumulating until the heap is full.
Fix:
- Code Fix: Modify the
Cacheclass to calldata.clear()at the start of the daily process. - Config Fix (Temporary): Increase the heap size to
-Xmx2gto buy time until the code can be fixed and deployed.
