理解 JVM 記憶體結構是排查 OOM、調優 GC、寫出高效能代碼的基石。本文依照 JVM 規範 (JLS/JVMS) 系統梳理運行時資料區劃分、物件分配邏輯、常量池演進與常見誤區。
+-------------------------+ | Heap (堆) | ← 所有執行緒共享、GC 主戰場、new 物件/陣列分配區 +-------------------------+ | Metaspace (元空間) | ← 類元資料、常量池 (Java 8 移出 Heap)、靜態變數、JIT 代碼緩存 | (原 PermGen/方法區) | ← 本機記憶體、受 MaxMetaspaceFree 控制 +-------------------------+ | Thread Stacks (棧) | ← 每執行緒私有、棧幀存局部變數/操作數棚/方法返回地址 | (虛擬機棧/本地方法棧) | ← -Xss 控制大小、遞歸過深拋 StackOverflowError +-------------------------+ | PC Register (程式計數器)| ← 每執行緒私有、指向當前位元碼指令 +-------------------------+ | Direct Memory (直接記憶體) | ← NIO DirectByteBuffer、Unsafe.allocateMemory、不受 Heap 限制 +-------------------------+
| 維度 | 棧 | 堆 |
|---|---|---|
| 存儲內容 | 基本型別局部變數、物件參考、方法調用幀 | 所有 new 出來的物件實例、陣列 |
| 執行緒隔離 | 私有 | 共享 |
| 生命週期 | 隨方法進出自動入棧/出棧 | GC 判定不可達才回收 |
| 異常 | StackOverflowError | OutOfMemoryError: Java heap space |
| 大小控制 | -Xss (預設 1MB) | -Xms/-Xmx |
| 存取速度 | 極快 (指針移動) | 較慢 (需 GC 管理) |
-XX:MaxPermSize 限制,String.intern() 易導致 PermGen OOMString.intern() 安全許多String 物件引用,"abc" 字面量編譯期入池,intern() 運行時入池new 指令觸發類載入 (未載入則先載入、驗證、準備、解析、初始化)<init> 建構子 (賦值、代碼塊)# 查看預設 Heap/Stack 大小
java -XX:+PrintFlagsFinal -version | grep -E 'HeapSize|ThreadStackSize'
# 運行時動態查看
jmap -heap
jcmd VM.flags
jstat -gcutil 1000
String.intern() 在 Java 7+ 安全,但大量動態字串入池仍會撐爆 HeapOutOfMemoryError: Direct buffer memory) 需調 -XX:MaxDirectMemorySize 或檢查 NIO/Netty 未釋放JVM 記憶體模型不是背誦考點,而是線上故障排查的地圖。熟記各區職責、參數控制、異常特徵,下次遇到 OOM/StackOverflow 才能在幾分鐘內定位根因,而非盲目加記憶體。