Java 虛擬機(JVM)是 Java 程式執行的基石,理解其記憶體模型與類加載流程,對效能調優、問題排查至關重要。本文系統梳理 JVM 物理結構、編譯執行流程、類加載器層級、執行引擎棧幀結構,以及運行時資料區的劃分與 OOM 場景。
JVM 屏蔽了底層硬體與作業系統差異,實現「一次編寫,到處執行」。除 Java 外,Scala、Kotlin、JRuby 等語言也運行於 JVM 之上。不同平台(Windows、Linux、macOS、Android)擁有各自的 JVM 實現。
| 加載器 | 職責 | 實現語言 |
| Bootstrap ClassLoader | 載入 $JAVA_HOME/jre/lib/rt.jar 核心類庫 | C++ |
| Extension ClassLoader | 載入 jre/lib/ext/*.jar 或 -Djava.ext.dirs 指定目錄 | Java |
| App ClassLoader | 載入 classpath 下的類 | Java |
| Custom ClassLoader | 應用自定義(如 Tomcat、JBoss) | Java |
雙親委派機制:檢查是否已加載時自底向上(Custom → Bootstrap);加載時自頂向下(Bootstrap → Custom),確保核心類只被加載一次,防止偽造核心類。
2.3 執行引擎與棧幀結構
JVM 採用基於棧的指令集(不同於 Android 的基於寄存器)。每個執行緒擁有獨立的程序計數器(PC)與 Java 虛擬機棧。
- 程序計數器:指向下一條位元碼指令偏移量,執行 Native 方法時為空。
- 棧幀:對應一次方法調用,包含局部變量表、操作數棧、動態連接、方法返回地址。

局部變量表
以 Slot(32 位)為單位存放基本類型、引用、returnAddress。long/double 佔兩個 Slot。索引 0 存放 this 引用(非 static 方法),參數緊接其後,再是局部變量。Slot 可重用,影響 GC 行為。
操作數棧
32 位類型佔 1 深度,64 位佔 2。位元碼指令透過入棧/出棧完成運算。最大深度編譯期確定。
動態連接
每個棧幀持有指向運行時常量池中該方法符號引用的指標,支援動態連接(運行期解析為直接引用)。
方法返回地址
正常返回:調用者 PC 計數器值;異常返回:由異常處理表決定。
三、運行時資料區與 OOM

3.1 程序計數器
線程私有,唯一無 OOM 規定的區域。
3.2 Java 虛擬機棧
線程私有,生命週期與線程同步。異常:StackOverflowError(棧深度超限)、OutOfMemoryError(動態擴展失敗,多線程下常見)。
3.3 本地方法棧
服務 Native 方法,功能類似 Java 棧。
3.4 Java 堆
所有線程共享,最大區域,幾乎所有對象實例與數組在此分配,GC 主戰場(「GC 堆」)。物理可不連續,邏輯連續。無法分配且無法擴展時拋 OutOfMemoryError。
3.5 方法區
線程共享,存放類資訊、常量、靜態變量、JIT 編譯代碼。HotSpot 稱「永久代」(JDK 8 移至 Metaspace),JRockit/IBM J9 無永久代概念。可選擇不實現 GC,主要回收廢棄常量與無用類。
運行時常量池:屬方法區一部分,具動態性,String.intern() 可將字串加入池中。
3.6 直接記憶體
非 JVM 運行時資料區,JDK 1.4 NIO 引入,通過 DirectByteBuffer 在堆外分配,避免堆與 Native 堆複製,受本機總記憶體限制,亦可能導致 OOM。
3.2 記憶體溢出與洩漏

- 棧大小與線程數權衡:OS 進程記憶體有限,扣除堆、方法區、PC、進程本身,剩餘給棧。單線程棧越大,可建線程越少。大量線程導致 OOM 時,可減小
-Xss 或堆大小換取更多線程。
- 記憶體洩漏 vs 溢出:洩漏指分配的記憶體失去引用無法回收(Java 少見,但持有不用的引用會造成);溢出指需求超過系統上限。
3.3 對象實例化與訪問方式
Object obj = new Object();
涉及 Java 棧(引用)、Java 堆(實例)、方法區(類型資訊)。訪問方式兩種:
- 句柄池:引用指向穩定句柄,GC 移動對象只改句柄指針,引用不變。

- 直接指針:引用直接指向對象,速度快,HotSpot 默認採用。

- 上一篇文章: Java 獲取系統時間與格式化完整指南
- 下一篇文章: 深入理解 Java volatile 關鍵字:可見性與原子性的邊界
Copyright ©
程式碼之家 All Rights Reserved