JVM原理
- JVM 核心架構與工作流程
- 1. 類加載機制(Class Loading)
- 2. 運行時數據區(Runtime Data Areas)
- 堆(Heap)
- 方法區(Method Area):元空間(Metaspace)公共區域
- 虛擬機棧(Java Stack):線程私有
- 程序計數器(PC Register):線程私有
- 本地方法棧:支持 JNI(如調用 C/C++ 庫)
- 執行引擎與性能優化
- 1. 字節碼執行
- 2. 垃圾回收(GC)機制
- 3. 內存分配策略
- 4.不同場景選擇合適的收集器組合:
- 內存泄漏(Memory Leak) 和 內存溢出(Out of Memory, OOM)
- 1.如何查看
- 2. 監控與診斷工具
- 1. 關鍵 JVM 參數
- 性能調優實戰
- 1. 關鍵 JVM 參數
- 2. 監控與診斷工具
- 3. 常見問題排查
- JVM 新特性與趨勢(2025)
- 總結
JVM
是Java Virtual Machine(Java虛擬機)的縮寫,JVM是一種用于計算設備的規范,它是一個
虛構出來的計算機
,是通過在實際的計算機上仿真模擬各種計算機功能來實現
為什么java可以跨平臺 原因就是有JVM
JVM 核心架構與工作流程
1. 類加載機制(Class Loading)
Java編譯器(javac).java文件 -->.class文件
而類加載過程:
加載:查找 .class 文件,生成二進制數據并創建 Class 對象
鏈接:包括
驗證
:檢查字節碼格式與安全性;
準備
:為靜態變量分配內存并賦默認值(如 int 初始化為 0);
解析
:將符號引用轉為直接引用(方法/字段的實際地址)
初始化:執行靜態代碼塊( 方法),為靜態變量賦實際值
這點可以參考static作用里面詳細介紹了
類加載器
啟動類加載器(Bootstrap):加載核心庫(java.lang 等),由 C++ 實現。
擴展類加載器(Extension):加載 jre/lib/ext 目錄的擴展類。
應用類加載器(AppClassLoader):加載用戶類路徑(ClassPath)的類。
自定義加載器:支持熱部署等場景
雙親委派模型:優先委派父加載器處理請求,避免核心類被篡改(如 java.lang.String)
2. 運行時數據區(Runtime Data Areas)
運行時數據區分類 以及對應存儲的內容
堆(Heap)
存儲對象實例
和數組
,分為 新生代(Eden + Survivor)和 老年代。
新生代
:新對象分配區,采用復制算法(Minor GC)
老年代
:長期存活對象區,采用標記-清除或標記-整理算法(Full GC)
方法區(Method Area):元空間(Metaspace)公共區域
JDK 8 后由 元空間(Metaspace)替代永久代
1.使用本地內存
存儲了被虛擬機加載的類元信息
、常量
、靜態變量
、即時編譯器編譯后的代碼
等
加載的類元信息
:包括類的名稱、方法信息、字段信息
方法區存儲方法代碼:方法區中存儲了方法的字節碼指令。當方法被調用時(即方法入口),JVM需要從方法區中讀取該方法的字節碼指令來執行,它是類級別的,與具體調用無關
2.元空間不在與堆是連續的物理內存,而是改成本內存,理論上只要本地內存足夠就不會出現OOM(OutOfMemoryError),從而盡可能避免OOfM
虛擬機棧(Java Stack):線程私有
虛擬機棧(Java Stack)存儲的是棧幀(Stack Frame)
,
在JVM中,每個方法在調用時都會在調用棧上創建一個棧幀(stack frame)
。當方法被調用時,棧幀被創建(在入口)
,當方法返回時(無論是正常返回還是異常返回),棧幀被銷毀(出口)
// 方法入口
public int exampleMethod(int a) {if (a < 0) {return 0; // 一個出口}System.out.println("Positive number: " + a);return a; // 另一個出口
}
而棧幀包含了:
局部變量表
:局部變量表用于存放編譯期可知的各種基本數據類型
、對象引用類型
和returnAddress類型數據
,最小存儲單位為變量槽
,編譯時就已確定,不受程序影響
操作數棧
:操作數棧也被稱為操作棧,它是一個LIFO棧
,編譯時就已確定,不受程序影響,操作數棧的每一個元素都可以是Java數據類型,32位數據類型所占棧容量為1,64位數據類型所占棧容量為2
動態連接
:每個棧幀都包含一個指向運行時常量池中該棧幀所屬方法的引用
,持有這個引用是為了支持方法調用過程中的動態連接
方法出口
:返回地址等信息,控制權返回給調用者的時刻,恢復調用者的棧幀和程序計數器
注意:棧深度過大引發 StackOverflowError;擴展失敗導致 OutOfMemoryError
方法區是方法的"靜態藍圖"存儲地(字節碼、元數據)
方法入口/出口是動態執行過程:
入口時讀取方法區數據創建棧幀 → 執行中依賴方法區指令 → 出口時釋放棧幀但保留方法區數據
二者共同構成Java方法執行的完整生命周期。
程序計數器(PC Register):線程私有
記錄當前執行指令地址,唯一不會發生 OOM 的區域
本地方法棧:支持 JNI(如調用 C/C++ 庫)
主要提供本地方法
本地方法來源區分
如果方法涉及硬件、系統資源或底層調度,通常由操作系統控制
如果方法涉及 Java 運行時環境的管理,通常由 JVM 操控,如調用 C/C++ 庫
執行引擎與性能優化
1. 字節碼執行
- 解釋器:逐行解釋字節碼,啟動快但效率低。
- 即時編譯器(JIT):就是
靜態編譯和動態編譯
- C1 編譯器:快速編譯熱點代碼(方法調用計數器閾值 ≈1萬次),輕度優化。
- C2 編譯器:深度優化熱點代碼(如逃逸分析、內聯優化),生成高效本地機器碼 。
- Falcon JIT(Azul Zulu Prime):基于 LLVM,性能優于傳統 C2 。
2. 垃圾回收(GC)機制
- 分代收集策略:
- 新生代:復制算法(對象從 Eden → Survivor 區復制)。
- 老年代:標記-清除(碎片多)或標記-整理(碎片少)。
- 主流垃圾收集器:
- Serial/Parallel:單線程/多線程,適合低延遲或高吞吐場景。
- CMS:并發標記清除,減少停頓時間(但存在碎片問題)。
- G1/ZGC:分區域收集,可預測停頓時間,兼顧吞吐與延遲 。
- GC 觸發條件:
- Minor GC:Eden 區滿時觸發。
- Full GC:老年代不足、元空間溢出或顯式調用
System.gc()
。
3. 內存分配策略
- 對象優先分配在 Eden 區:空間不足時觸發 Minor GC。
- 大對象直入老年代:避免在新生代頻繁復制(如長數組)。
- 長期存活對象晉升:對象年齡(Survivor 區存活次數)超閾值(默認 15)進入老年代
4.不同場景選擇合適的收集器組合:
收集器 | 區域 | 算法 | 特點 |
---|---|---|---|
Serial | 新生代 | 標記-復制 | 單線程,簡單高效(Client模式) |
Parallel Scavenge | 新生代 | 標記-復制 | 多線程,吞吐量優先 |
ParNew | 新生代 | 標記-復制 | 多線程版Serial,配合CMS使用 |
CMS | 老年代 | 標記-清除 | 并發收集,低延遲(JDK9棄用) |
G1 | 全堆 | 分區域標記-整理 | 可控停頓時間,JDK9+默認 |
ZGC | 全堆 | 染色指針+讀屏障 | <10ms停頓,TB級堆(JDK15+) |
內存泄漏(Memory Leak) 和 內存溢出(Out of Memory, OOM)
內存泄漏(Memory Leak)
和內存溢出(Out of Memory, OOM)
是兩種不同的內存管理問題
內存泄漏
是指程序在申請內存后無法釋放已申請的內存空間
,導致系統可用內存逐漸減少,比如一個對象被其余對象引用導致垃圾回收器無法回收該對象所占用的內存,內存泄漏可能會隨著時間的推移逐漸惡化,最終可能導致程序性能下降或崩潰
內存溢出
則是指程序在運行過程中申請的內存超出了系統分配給它的最大內存限制
。這可能是由于程序需要處理的數據量過大,或者程序中存在無限循環或遞歸調用等問題。當發生內存溢出時,程序會無法繼續執行,并拋出相應的錯誤提示
1.如何查看
2. 監控與診斷工具
- jstat:查看GC統計 ,監控堆內存與 GC 情況(如
jstat -gcutil <pid> 1000
) - jmap:生成堆轉儲快照(Heap Dump),分析內存泄漏(
jmap -dump:format=b,file=heap.bin <pid>
)
1. 關鍵 JVM 參數
性能調優實戰
1. 關鍵 JVM 參數
-Xms2g -Xmx2g # 堆初始/最大內存(避免動態擴容)
-XX:NewRatio=2 # 新生代:老年代 = 1:2
-XX:MaxMetaspaceSize=256m # 限制元空間大小
-XX:+UseG1GC # 啟用 G1 收集器
-XX:MaxGCPauseMillis=200 # 目標最大停頓時間
2. 監控與診斷工具
- jstat:查看GC統計 ,監控堆內存與 GC 情況(如
jstat -gcutil <pid> 1000
) - jmap:生成堆轉儲快照(Heap Dump),分析內存泄漏(
jmap -dump:format=b,file=heap.bin <pid>
) - jstack:抓取線程快照,定位死鎖(
jstack -l <pid>
) - Arthas:阿里開源工具,支持實時診斷 OOM、熱更新代碼
3. 常見問題排查
- OOM(OutOfMemoryError):
- 堆溢出:
java.lang.OutOfMemoryError: Java heap space
→ 增大-Xmx
或分析對象生命周期 - 元空間溢出:
java.lang.OutOfMemoryError: Metaspace
→ 調整-XX:MaxMetaspaceSize
- 堆溢出:
- 頻繁 Full GC:
- 老年代空間不足 → 檢查對象晉升策略或內存泄漏
- 元空間不足 → 優化類加載或調整元空間大小
JVM 新特性與趨勢(2025)
1.虛擬線程(Project Loom):
- 輕量級線程(非 OS 線程),提升高并發應用性能(如單機支持百萬級連接)
2.ZGC 低延遲優化:
- 停頓時間控制在 1ms 內,適用于金融、實時系統 。
3.AOT 編譯(GraalVM):
- 將字節碼預先編譯為本地機器碼,加速啟動速度(適合 Serverless 場景)
總結
JVM 作為 Java 生態的基石,通過 類加載、內存管理、JIT 即時編譯 和 GC 回收 實現跨平臺與高性能。掌握其原理可有效解決 OOM、GC 停頓等問題,提升系統穩定性。未來趨勢聚焦于低延遲(ZGC)、輕量并發(虛擬線程)及原生編譯(GraalVM),持續推動 Java 在云原生場景的應用 。