以下是JVM相关高频面试问题及简明解答:
一、内存模型
- 内存区域划分
- 线程私有:程序计数器(记录执行位置)、虚拟机栈(方法调用栈帧)、本地方法栈
- 线程共享:堆(对象实例)、方法区(类信息/常量,JDK8后称元空间)
- 直接内存:NIO使用的堆外内存
二、垃圾回收
-
GC可达性分析算法
- 从GC Roots对象(线程栈变量/静态变量/常量等)出发,标记不可达对象
- 三色标记法:白(未处理)、灰(处理中)、黑(已处理)
-
常见的垃圾收集器
- Serial:单线程收集,新生代复制算法
- CMS:并发标记清除,追求低停顿
- G1:分区收集,可预测停顿时间模型
- ZGC:JDK11+,使用染色指针技术,停顿<10ms
三、类加载机制
-
双亲委派模型
- 加载请求逐级向上委托,避免重复加载核心类
-
打破双亲委派的场景
- SPI机制(如JDBC驱动加载)
- 热部署实现(OSGi框架)
- 自定义类加载器重写loadClass方法
四、性能调优
-
常见OOM类型
- 堆溢出:
java.lang.OutOfMemoryError: Java heap space
- 元空间溢出:
java.lang.OutOfMemoryError: Metaspace
- 线程栈溢出:
java.lang.StackOverflowError
- 堆溢出:
-
内存泄漏排查
- 使用
jmap -histo:live <pid>
查看对象分布 - MAT工具分析heap dump
- 示例代码:
// 错误示例:静态Map持续增长 public class LeakDemo { static Map<Object,Object> cache = new HashMap<>(); void add(Object key, Object value){ cache.put(key, value); } }
- 使用
五、执行引擎
- 即时编译器(JIT)
- 热点代码检测:方法调用计数器/回边计数器
- 编译优化:逃逸分析、锁消除、标量替换等
六、实战问题
-
如何设置年轻代大小?
- 建议参数:
-XX:NewRatio=2
(老年代与年轻代比例) - 监控工具:
jstat -gcutil <pid> 1000
- 建议参数:
-
线上Full GC频繁如何排查?
jstat -gcutil
查看GC频率jmap -dump:format=b,file=heap.bin <pid>
获取堆快照- 分析大对象/内存泄漏
- 调整参数:
-XX:+UseG1GC
或增大堆空间
注:回答时建议结合具体场景举例,如根据应聘者简历中的项目经历,关联实际调优案例进行说明效果更佳。