理解 JVM 內存結構與垃圾回收原理,是定位 OOM、優化 GC 停頓、排查內存洩漏的基石。本文覆蓋運行時數據區、對象創建與訪問、GC 根枚舉、可達性分析算法、引用計數缺陷及現代收集器演進。


| 區域 | 線程共享性 | 核心內容 | 異常 |
|---|---|---|---|
| 堆 | 共享 | 對象實例、數組(GC 主戰場) | OutOfMemoryError: Java heap space |
| 虛擬機棧 | 私有 | 棧幀(局部變量表、操作數棧、動態鏈接、返回地址) | StackOverflowError / OOM |
| 本地方法棧 | 私有 | Native 方法調用 | 同上 |
| 方法區 | 共享 | 類元數據、常量、靜態變量、JIT 編譯代碼(元空間) | OOM: Metaspace |
| 程序計數器 | 私有 | 當前執行字節碼行號 | 無 |
new 指令 → 類加載檢查 → 堆劃分內存(指針碰撸/空閒列表) → 初始化零值 → 設置對象頭(哈希碼、GC 信息、鎖狀態、類元數據指針) → 執行 <init> 構造器。reference → 堆對象。主流兩種方式:
早期嘗試:對象頭維護計數器,引用+1,釋放-1,歸零回收。致命缺陷:無法解決 循環引用(A→B,B→A,無外部引用時計數不為零)。
以 GC Roots 為起點,向下搜索引用鏈,可達對象存活,不可達對象判定為垃圾。
GC Roots 包含:
| 引用類型 | 強度 | GC 行為 | 典型場景 |
|---|---|---|---|
| 強引用 | 最強 | 永不回收 | Object obj = new Object() |
| 軟引用 | 次強 | 內存不足前回收 | 緩存(SoftReference + HashMap) |
| 弱引用 | 較弱 | 下次 GC 回收 | WeakHashMap、ThreadLocalMap |
| 虛引用 | 最弱 | 跟蹤對象被回收時機 | PhantomReference + ReferenceQueue(直接內存釋放) |
| 算法 | 原理 | 優點 | 缺點 | 應用代 |
|---|---|---|---|---|
| 標記-清除 | 標記存活 → 清除死對象 | 實現簡單 | 碎片化、效率低 | 老年代(CMS 初期) |
| 標記-整理 | 標記 → 移動存活對象一端 → 清理邊界外 | 無碎片 | 移動開銷大 | 老年代(Parallel Old、G1 混合) |
| 複製 | 劃分兩塊,每次用一塊,存活複製到另一塊 | 無碎片、分配快 | 內存利用率 50% | 新生代(Eden + Survivor) |
分代假說:多數對象朝生夕死(新生代用複製),少數熬過多次 GC 進老年代(老年代用標記-整理/混合)。
jmap -heap pid 查看堆佈局與 GC 策略。jstat -gcutil pid 1000 監控各代使用率與 GC 頻率。jmap -histo:live pid | head -20 定位大對象/洩漏候選。MAT / JVisualVM 分析 Heap Dump,追蹤 GC Roots 鏈路定位洩漏根因。