概述
Java 虛擬機在執行程序時,將管理的內存劃分為若干個數據區域。每個區域有特定用途、創建與銷毀時機。理解這些區域是排查 OOM、優化性能、掌握 GC 原理的基礎。本文基於 JDK 8 之前的經典模型(含永久代)進行詳細講解。
運行時數據區域總覽

一、程序計數器
- 特點:線程私有、內存極小、唯一不拋出
OutOfMemoryError 的區域。
- 作用:當前線程執行的字節碼行號指示器。分支、循環、跳轉、異常處理、線程恢復均依賴它。
- 值:執行 Java 方法時指向字節碼指令地址;執行 Native 方法時為 undefined。
二、Java 虛擬機棧
- 生命周期:與線程同步,線程私有。
- 棧幀:每個方法調用對應一個棧幀,包含局部變量表、操作數棧、動態鏈接、方法出口。
- 局部變量表:存放編譯期可知的基本類型、對象引用、returnAddress。大小在編譯期確定,運行期不變。long/double 佔 2 Slot,其餘 1 Slot。
- 異常:
StackOverflowError:棧深度超過限制(遞歸過深)。
OutOfMemoryError:動態擴展時無法申請足夠內存。
三、本地方法棧
與虛擬機棧類似,但服務於 Native 方法(JNI 調用)。HotSpot 將兩者合併實現。同樣可能拋出上述兩種異常。
四、Java 堆
- 地位:JVM 管理的最大內存區域,所有線程共享,啟動時創建。
- 用途:存放對象實例(幾乎所有對象)。隨著 JIT 編譯與逃逸分析成熟,棧上分配、標量替換使「對象必須在堆上」不再絕對。
- GC 主戰場:堆是垃圾收集器管理的核心區域,故稱「GC 堆」。
- 分代結構:新生代 + 老年代。新生代細分 Eden、From Survivor、To Survivor。
- 內存分配:邏輯連續、物理可不連續。主流 JVM 可擴展實現(-Xms/-Xmx)。無法分配時拋出
OutOfMemoryError。
五、方法區
- 共享區域:存儲已加載的類信息、常量、靜態變量、JIT 編譯後的代碼。
- 別名:Non-Heap(非堆),區別於 Java 堆。
- 規範寬鬆:可固定大小、可擴展、可不實現 GC。
- GC 行為:較少,主要針對常量池回收與類型卸載。回收條件苛刻,效果難以滿意。
- 異常:無法滿足內存分配時拋出
OutOfMemoryError。
六、運行時常量池
- 屬於方法區一部分。
- Class 文件常量池存放編譯期字面量與符號引用;類加載後進入運行時常量池。
- 動態性:運行期可添加新常量(如
String.intern())。
- 受方法區內存限制,不足時拋出
OutOfMemoryError。
七、對象訪問定位
代碼 Object obj = new Object(); 涉及三大區域:
- Java 棧:
obj 引用存放在局部變量表(reference 類型)。
- Java 堆:
new Object() 分配實例數據,含對象頭、實例數據、對齊填充。
- 方法區:存放類型信息(類元數據),堆中對象頭包含指向方法區類型的指針。
主流訪問方式
| 方式 | 原理 | 優點 | 缺點 |
| 句柄訪問 |
堆劃分句柄池,reference→句柄地址,句柄→實例/類型地址 |
對象移動時只改句柄,reference 穩定 |
雙重間接訪問,速度較慢 |
| 直接指針 |
reference 直接存對象地址,對象頭含類型指針 |
節省一次指針定位,速度快(HotSpot 採用) |
對象移動需更新 reference |


總結
掌握 JVM 內存結構是 Java 進階的必經之路。程序計數器與棧隨線程生死,堆與方法區隨進程;堆是 GC 核心,方法區存類元數據;對象訪問方式影響性能,HotSpot 採用直接指針。理解這些為後續學習 GC 算法、調優參數、排查內存洩漏打下堅實基礎。