JVM整體架構
JVM(Java Virtual Machine)是Java程序運行的核心環境,主要由以下幾個部分組成:
1. 程序計數器(Program Counter)
- 特點:線程私有,每個線程都有獨立的程序計數器
- 作用:記錄當前線程正在執行的字節碼指令地址(行號)
- 重要性:支持線程切換和恢復,確保程序能夠正確執行
2. 虛擬機棧(JVM Stack)
- 特點:線程私有,采用先進后出(LIFO)的數據結構
- 組成:由多個棧幀組成,每個方法調用創建一個棧幀
- 棧幀內容:局部變量表、操作數棧、動態鏈接、方法返回地址
虛擬機棧常見問題
垃圾回收是否涉及棧內存?
不涉及。垃圾回收主要針對堆內存,棧幀在方法執行完畢后自動彈出釋放內存。
棧內存分配越大越好嗎?
不是。默認棧內存通常為1024KB,過大會導致可創建的線程數減少。例如:512MB總內存,1024KB棧大小可創建512個線程,改為2048KB則只能創建256個線程。
方法內的局部變量是否線程安全?
- 如果局部變量沒有逃離方法作用范圍,則線程安全
- 如果局部變量引用了對象并逃離方法作用范圍,需要考慮線程安全問題
棧內存溢出情況
- 棧幀過多導致溢出(典型:無限遞歸調用)
- 單個棧幀過大導致溢出
3. 堆內存(Heap)
- 特點:線程共享區域,JVM中最大的內存區域
- 作用:存儲對象實例和數組
- 異常:當堆內存不足時拋出OutOfMemoryError異常
- 分代結構:通常分為年輕代(Eden、Survivor)和老年代
4. 方法區(Method Area)
- 特點:線程共享的內存區域
- 存儲內容:類信息、運行時常量池、靜態變量、編譯后的代碼
- 生命周期:虛擬機啟動時創建,關閉時釋放
- 異常:內存不足時拋出OutOfMemoryError: Metaspace
JDK版本差異
JDK 1.7 vs 1.8的重要變化:
- JDK 1.7:存在永久代(PermGen),存儲類信息、靜態變量、常量等
- JDK 1.8:移除永久代,改為元空間(Metaspace),使用本地內存,有效防止內存溢出
5. 直接內存(Direct Memory)
- 特點:不屬于JVM內存結構,不由JVM管理
- 本質:使用系統內存,常見于NIO操作
- 特性:分配回收成本較高,但讀寫性能優秀
- 用途:主要用作數據緩沖區
內存管理要點
垃圾回收機制
- 主要針對堆內存進行回收
- 棧內存隨方法調用結束自動釋放
- 方法區在JDK 1.8后使用元空間,減少OOM風險
線程安全考慮
- 程序計數器、虛擬機棧:線程私有,天然線程安全
- 堆內存、方法區:線程共享,需要考慮并發訪問問題
- 直接內存:需要程序員手動管理,注意內存泄漏
面試重點
- 內存區域劃分:能清楚說明各個內存區域的作用和特點
- 線程安全性:理解哪些區域是線程私有的,哪些是共享的
- 內存溢出:了解各種OutOfMemoryError的產生原因和解決方案
- JDK版本差異:特別是永久代到元空間的變化
- 性能調優:理解不同內存區域對性能的影響
補充知識點
常見JVM參數
-Xms
:初始堆大小-Xmx
:最大堆大小-Xss
:棧大小-XX:MetaspaceSize
:元空間初始大小-XX:MaxMetaspaceSize
:元空間最大大小
內存泄漏常見場景
- 長生命周期對象持有短生命周期對象的引用
- 靜態集合類引用大量對象
- 監聽器和回調函數未正確移除
- 線程池中的線程未正確關閉