深入理解 JVM 內存結構是排查 OOM、優化 GC、調優性能的基石。本文系統梳理運行時數據區域、對象存活判定算法、引用類型體系、四大經典 GC 算法及分代收集策略。

| 區域 | 線程共享性 | 核心功能 | 異常類型 |
|---|---|---|---|
| 程序計數器 | 私有 | 當前線程執行字節碼行號指示器 | 無 OOM |
| 虛擬機棧 | 私有 | 存儲棧幀(局部變量表、操作數棧、動態鏈接、方法出口) | StackOverflowError / OOM |
| 本地方法棧 | 私有 | 服務 Native 方法 | StackOverflowError / OOM |
| Java 堆 | 共享 | 對象實例分配主戰場,GC 主管理區域 | OutOfMemoryError |
| 方法區 | 共享 | 類信息、常量、靜態變量、JIT 編譯代碼 | OutOfMemoryError |
| 運行時常量池 | 共享 | 方法區一部分,存編譯期字面量/符號引用,String.intern() 動態擴展 |
OutOfMemoryError |
註:JDK 1.8 移除永久代,元空間使用本地內存;字符串常量池移至堆中。

finalize()(未覆寫或已執行過視為不需執行)。finalize() 最多被自動調用一次。public class FinalizeEscapeGC {
public static FinalizeEscapeGC SAVE_HOOK = null;
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("finalize method executed!");
SAVE_HOOK = this; // 自救:重新建立 GC Roots 引用
}
public static void main(String[] args) throws Throwable {
SAVE_HOOK = new FinalizeEscapeGC();
SAVE_HOOK = null;
System.gc(); Thread.sleep(500);
System.out.println(SAVE_HOOK != null ? "存活" : "死亡"); // 存活
SAVE_HOOK = null;
System.gc(); Thread.sleep(500);
System.out.println(SAVE_HOOK != null ? "存活" : "死亡"); // 死亡(二次不再調用 finalize)
}
}
| 引用類型 | 強度 | 回收時機 | 典型用途 | API |
|---|---|---|---|---|
| 強引用 | 最強 | 永不回收 | 常規對象 Object o = new Object() |
— |
| 軟引用 | 次強 | 內存不足前(二次回收) | 緩存(圖片、網頁內容) | SoftReference |
| 弱引用 | 較弱 | 下次 GC 時 | 監聽器、回調、ThreadLocalMap | WeakReference |
| 虛引用 | 最弱 | 被回收時收到通知 | 直接內存釋放追蹤、清理動作 | PhantomReference |
// 軟引用緩存示例
SoftReference<Bitmap> cache = new SoftReference<>(loadBitmap());
Bitmap bmp = cache.get();
if (bmp == null) { // 已被回收
bmp = loadBitmap();
cache = new SoftReference<>(bmp);
}
| 算法 | 核心思想 | 優點 | 缺點 | 適用區域 |
|---|---|---|---|---|
| 標記-清除 | 標記存活,清除死亡 | 老年代(配合整理) | ||
| 復制 | 劃分兩塊,存活對象復制到另一塊 | 新生代 | ||
| 標記-整理 | 標記後向一端移動存活對象,清理邊界外 | 老年代 | ||
| 分代收集 | 按對象壽命劃分新生代/老年代,不同代用不同算法 | 整個堆(現代 JVM 主流) |
-XX:PretenureSizeThreshold)。gc/ 目錄下各收集器實現