理解 JVM 內存結構是排查 OOM、調優 GC 的基石。本文依據 JVM 規範劃分 5 大運行時數據區,補充直接內存,並給出各區域 OOM 觸發代碼、對象創建內存佈局、訪問方式對比。
| 區域 | 線程性質 | 核心內容 | OOM 條件 |
|---|---|---|---|
| 程序計數器 | 私有 | 字節碼行號指示器 | 規範定義無 OOM |
| 虛擬機棧 | 私有 | 棧幀(局部變量表、操作數棧、動態鏈接、返回地址) | 棧深度溢出 → StackOverflowError;擴展失敗 → OOM |
| 本地方法棧 | 私有 | Native 方法調用 | 同虛擬機棧 |
| Java 堆 | 共享 | 幾乎所有對象實例、數組 | 無內存可分配且無法擴展 → OOM |
| 方法區 | 共享 | 類信息、常量、靜態變量、JIT 編譯代碼、運行時常量池 | 無法滿足分配 → OOM |
ByteBuffer.allocateDirect() 向 OS 申請。MaxDirectMemorySize 控制)。-Xms20m -Xmx20m -XX:+HeapDumpOnOutOfMemoryError
List list = new ArrayList<>();
while (true) list.add(new byte[1024 * 1024]); // 1MB 對象
-Xss256k
while (true) new Thread(() -> { try { Thread.sleep(10000); } catch (Exception e) {} }).start();
-XX:PermSize=10m -XX:MaxPermSize=10m (JDK 7)
-XX:MetaspaceSize=10m -XX:MaxMetaspaceSize=10m (JDK 8+)
// 不斷生成類:CGLIB/動態代理/JSP 編譯
while (true) Enhancer.create(Object.class, MethodInterceptor.class);
-Xmx20m -XX:MaxDirectMemorySize=10m
ByteBuffer.allocateDirect(1024 * 1024); // 循環分配
Object obj = new Object();
涉及三區:
reference(obj)。| 方式 | 原理 | 優點 | HotSpot 採用 |
|---|---|---|---|
| 句柄池 | reference → 句柄池 → 實例/類型信息 | 對象移動只改句柄,reference 穩定 | 否 |
| 直接指針 | reference → 實例地址(對象頭含類型指針) | 少一次間接訪問,速度快 | 是 |
-Xss 設置單線程棧大小。棧越大 → 可建線程越少。進程內存 = 堆 + 方法區 + 直接內存 + 線程數 × 棧大小 + OS 開銷。線程過多導致 OOM 時,若不能減線程,需減小 -Xmx 或 -Xss 換取更多線程槽位。
掌握內存區域邊界與 OOM 特徵,配合 jmap/jstack/jstat/Arthas 等工具,可快速定位生產環境內存問題。