理解 JVM 運行時數據區是排查 OOM 的前提。本文通過可運行代碼,分別觸發 Java 堆、虛擬機棧、方法區/元空間、運行時常量池、直接內存 五大區域的 OutOfMemoryError,並解析異常特徵與排查思路。
StackOverflowError,創建過多線程拋 OOM。DirectByteBuffer 分配,受 -XX:MaxDirectMemorySize 限制。核心:不斷創建對象 + 保持 GC Root 可達。
// VM Args: -Xms20m -Xmx20m
public class HeapOOM {
static class OOMObject {}
public static void main(String[] args) {
List<Object> list = new ArrayList<>();
while (true) list.add(new OOMObject()); // GC Root 為 list 靜態引用
}
}
異常特徵:java.lang.OutOfMemoryError: Java heap space,堆轉儲文件分析可見大量 OOMObject。
// VM Args: -Xss256k
public class StackOverflow {
private int depth = 0;
void leak() { depth++; leak(); }
public static void main(String[] args) {
try { new StackOverflow().leak(); }
catch (Throwable e) { System.out.println("棧深度: " + new StackOverflow().depth); throw e; }
}
}
異常:java.lang.StackOverflowError,非 OOM 但屬棧內存耗盡。
// VM Args: -Xss256k
public class ThreadOOM {
void dontStop() { while (true) {} }
public static void main(String[] args) {
while (true) new Thread(new ThreadOOM()::dontStop).start();
}
}
警告:Windows 下極易導致系統假死,謹慎運行!Linux 下受 ulimit -u、/proc/sys/kernel/pid_max 限制。
原理:動態生成大量類(CGLib、JSP、動態代理、OSGi 等場景)。JDK 8+ 設置 -XX:MaxMetaspaceSize。
// VM Args: -XX:MaxMetaspaceSize=10m
// 需引入 cglib 依賴
public class MetaspaceOOM {
public static void main(String[] args) {
while (true) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(OOMObject.class);
enhancer.setUseCache(false);
enhancer.setCallback((MethodInterceptor) (obj, method, args1, proxy) -> proxy.invokeSuper(obj, args1));
enhancer.create(); // 每次生成一個新子類 Class
}
}
static class OOMObject {}
}
異常:java.lang.OutOfMemoryError: Metaspace(JDK 8+),JDK 7 顯示 PermGen space。
JDK 6 永久代:String.intern() 會將字符串放入永久代常量池,限制 -XX:PermSize=10M -XX:MaxPermSize=10M 可觸發 PermGen space。
JDK 7/8:字符串常量池已移至堆,上述代碼會觸發 Java heap space,而非方法區 OOM。需關注版本差異。
觸發:ByteBuffer.allocateDirect(100MB) 循環調用,超過 -XX:MaxDirectMemorySize(默認等於 -Xmx)。
異常:java.lang.OutOfMemoryError: Direct buffer memory。
排查:NIO、Netty、RocketMQ 等大量使用直接內存的組件易發生泄漏,需檢查 Cleaner、Reference 釋放邏輯。
jps -l 定位進程 PIDjstat -gcutil PID 1000 觀察 GC 頻率、各區佔用jmap -dump:live,format=b,file=heap.hprof PID 導出堆轉儲MAT (Memory Analyzer Tool) / JProfiler 分析支配者樹、GC Roots 路徑jstack PID 分析線程狀態、死鎖、棧深度