JVM常見考題總結
1. JVM基礎概念
Q1: 什么是JVM?JVM、JRE、JDK的關系是什么?
答案:
- JVM: Java虛擬機,Java程序的運行環境
- JRE: Java運行時環境 = JVM + 核心類庫
- JDK: Java開發工具包 = JRE + 編譯器 + 調試工具
關系: JDK > JRE > JVM
Q2: Java程序的執行過程是什么?
答案:
Java源碼(.java) → javac編譯 → 字節碼(.class) → JVM解釋/編譯 → 機器碼
2. JVM內存結構
Q3: JVM內存區域有哪些?各自的作用是什么?
答案:
- 程序計數器: 記錄當前線程執行的字節碼行號
- 虛擬機棧: 存儲方法調用的局部變量和操作數棧
- 本地方法棧: 為Native方法服務
- 堆內存: 存儲對象實例,分為新生代和老年代
- 方法區: 存儲類信息、常量、靜態變量(Java 8后改為元空間)
Q4: 堆內存的結構是什么?
答案:
堆內存
├── 新生代 (1/3)
│ ├── Eden區 (8/10)
│ ├── Survivor0 (1/10)
│ └── Survivor1 (1/10)
└── 老年代 (2/3)
Q5: 什么是棧溢出?什么是堆溢出?
答案:
- 棧溢出 (StackOverflowError): 方法調用層次太深,棧空間不足
- 堆溢出 (OutOfMemoryError): 創建對象過多,堆空間不足
示例:
// 棧溢出
public void recursion() {
recursion(); // 無限遞歸
}
// 堆溢出
List<Object> list = new ArrayList<>();
while(true) {
list.add(new Object()); // 不斷創建對象
}
3. 垃圾回收
Q6: 什么是垃圾回收?如何判斷對象可以被回收?
答案:
垃圾回收: 自動釋放不再使用的對象內存
判斷方法:
- 引用計數法: 統計對象被引用的次數(有循環引用問題)
- 可達性分析: 從GC Roots開始,不可達的對象可以回收
GC Roots包括:
- 虛擬機棧中的引用
- 方法區中的靜態引用
- 方法區中的常量引用
- 本地方法棧中的引用
Q7: 常見的垃圾回收算法有哪些?
答案:
- 標記-清除: 標記垃圾對象,然后清除(產生內存碎片)
- 標記-復制: 將存活對象復制到另一塊內存(適合新生代)
- 標記-整理: 標記后將存活對象向一端移動(適合老年代)
- 分代收集: 新生代用復制算法,老年代用標記-整理
Q8: 常見的垃圾回收器有哪些?
答案:
- Serial GC: 單線程,適合小應用
- Parallel GC: 多線程,適合吞吐量優先
- CMS GC: 并發收集,適合響應時間優先
- G1 GC: 低延遲,適合大堆內存
- ZGC/Shenandoah: 超低延遲收集器
4. 類加載機制
Q9: 類加載的過程是什么?
答案:
加載 → 驗證 → 準備 → 解析 → 初始化 → 使用 → 卸載
詳細說明:
- 加載: 將.class文件讀入內存
- 驗證: 檢查字節碼格式和語義
- 準備: 為靜態變量分配內存并設置默認值
- 解析: 將符號引用轉換為直接引用
- 初始化: 執行靜態代碼塊和靜態變量賦值
Q10: 什么是雙親委派模型?
答案:
定義: 類加載器收到加載請求時,先委派給父加載器,父加載器無法加載時才自己加載
三層結構:
Bootstrap ClassLoader (啟動類加載器)
↑
Extension ClassLoader (擴展類加載器)
↑
Application ClassLoader (應用類加載器)
優點:
- 保證Java核心類庫的安全性
- 避免類的重復加載
Q11: 什么時候會觸發類的初始化?
答案:
- 創建類的實例
- 訪問類的靜態變量或方法
- 反射調用類
- 初始化子類時先初始化父類
- JVM啟動時的主類
5. JVM調優
Q12: 常用的JVM參數有哪些?
答案:
內存設置:
-Xms512m # 初始堆大小
-Xmx2g # 最大堆大小
-Xmn256m # 新生代大小
-XX:MetaspaceSize=128m # 元空間初始大小
垃圾回收器:
-XX:+UseG1GC # 使用G1收集器
-XX:+UseConcMarkSweepGC # 使用CMS收集器
-XX:+UseParallelGC # 使用Parallel收集器
調試參數:
-XX:+PrintGC # 打印GC信息
-XX:+HeapDumpOnOutOfMemoryError # OOM時生成堆轉儲
Q13: 如何分析和解決內存泄漏?
答案:
分析工具:
- jstat: 監控GC情況
- jmap: 生成堆轉儲文件
- MAT/VisualVM: 分析堆轉儲文件
常見內存泄漏:
- 靜態集合持有對象引用
- 監聽器未正確移除
- 數據庫連接未關閉
- ThreadLocal未清理
Q14: 如何進行JVM性能調優?
答案:
調優步驟:
- 監控應用性能指標
- 分析GC日志
- 調整堆內存大小
- 選擇合適的垃圾回收器
- 調整GC參數
- 驗證調優效果
調優原則:
- 優先調整內存大小
- 選擇合適的垃圾回收器
- 減少Full GC頻率
- 平衡吞吐量和延遲
6. 實際問題
Q15: 解釋以下代碼的執行結果
public class Test {
static {
System.out.println("靜態代碼塊");
}
{
System.out.println("實例代碼塊");
}
public Test() {
System.out.println("構造方法");
}
public static void main(String[] args) {
new Test();
new Test();
}
}
答案:
靜態代碼塊
實例代碼塊
構造方法
實例代碼塊
構造方法
解釋: 靜態代碼塊只在類初始化時執行一次,實例代碼塊在每次創建對象時執行
Q16: String s = new String("abc")創建了幾個對象?
答案:
可能創建1個或2個對象:
- 如果字符串常量池中已有"abc",創建1個對象(堆中的String對象)
- 如果字符串常量池中沒有"abc",創建2個對象(常量池中的"abc" + 堆中的String對象)
Q17: 什么是內存屏障?volatile關鍵字的作用是什么?
答案:
內存屏障: 防止指令重排序的機制
volatile作用:
- 保證可見性:修改對所有線程立即可見
- 禁止指令重排序
- 不保證原子性
7. 高級話題
Q18: 什么是逃逸分析?
答案:
逃逸分析: 分析對象的作用域,判斷對象是否會"逃逸"出方法或線程
優化效果:
- 棧上分配:不逃逸的對象可以在棧上分配
- 標量替換:將對象拆分為基本類型
- 同步消除:消除不必要的同步操作
Q19: 什么是TLAB?
答案:
TLAB (Thread Local Allocation Buffer): 線程本地分配緩沖區
作用:
- 每個線程在Eden區有自己的分配空間
- 避免多線程分配對象時的競爭
- 提高對象分配效率
Q20: JVM如何處理異常?
答案:
異常處理機制:
- 異常表:記錄try-catch塊的范圍和處理器
- 棧展開:異常發生時逐層向上查找處理器
- 異常對象:在堆中創建異常對象
性能影響:
- 異常創建成本高(需要填充棧軌跡)
- 不要用異常控制正常流程
浙公網安備 33010602011771號