JVM 面试题:面试官到底想考什么
大多数 JVM 面试题的背后,面试官想考察的是:你能不能把问题讲清楚。
概念背熟没用,你得能用自己的话把原理说出来,还要能画图、能举例。
JVM 的核心问题:内存管理
JVM 里发生的大部分问题——OOM、GC 频繁、性能抖动——最终都指向同一个根源:内存分配和回收。
先搞清楚 JVM 运行时数据区:
堆 Heap
├── Young Generation(年轻代)
│ ├── Eden
│ └── Survivor x2(S0, S1)
└── Old Generation(老年代)
方法区 Metaspace(替代了永久代)
虚拟机栈 VM Stack
本地方法栈 Native Stack
程序计数器 PC RegisterJDK vs JRE vs JVM:到底什么关系?
- JVM 是最底层,只运行字节码
- JRE 在 JVM 基础上加了标准类库,运行 Java 程序够用
- JDK 在 JRE 基础上加了编译器(javac)和工具,供开发者使用
垃圾回收:GC 在做什么
GC 要解决三个问题:哪些对象要回收?什么时候回收?怎么回收?
如何判断对象已死?
引用计数法(不用,因为循环引用无法回收)→ 可达性分析(通过 GC Roots 向下搜索,标记不可达对象)
GC Roots 包括:栈中的引用对象、方法区中静态/常量引用的对象、本地方法栈中的引用。
垃圾回收算法
| 算法 | 原理 | 优点 | 缺点 |
|---|---|---|---|
| 标记-清除 | 先标记再清除已死亡对象 | 基础简单 | 产生内存碎片 |
| 复制 | 分两区,用完把存活对象复制过去 | 没有碎片,速度快 | 只能用到一半空间 |
| 标记-整理 | 标记后把存活对象移到一起 | 没有碎片,利用率高 | 移动成本高 |
年轻代用复制算法(对象存活率低),老年代用标记-整理(对象存活率高)。
常见 GC
| 收集器 | 特点 | 使用场景 |
|---|---|---|
| Serial | 单线程,Stop the World | 客户端,内存小 |
| ParNew | Serial 的多线程版 | 年轻代,配合 CMS |
| Parallel Scavenge | 吞吐量优先 | 后台运算,不需要交互 |
| CMS | 并发标记清除 | 追求低停顿 |
| G1 | 区域化 GC | JDK 9+ 默认,替代 CMS |
G1 的核心思想:把堆分成多个等大的 Region,优先回收垃圾最多的 Region。停顿时间可控,是 JDK 11+ 的默认选择。
面试高频追问
Q: 堆 vs 栈?
| 区域 | 存储内容 | 线程共享 | 垃圾回收 |
|---|---|---|---|
| 堆 | 对象实例、数组 | 是 | GC 的主战场 |
| 栈 | 方法调用、局部变量 | 否(线程私有) | 自动管理 |
Q: 对象创建过程?
java
// 1. 检查类是否加载(类加载检查)
// 2. 分配内存(指针碰撞 / 空闲列表)
// 3. 初始化零值
// 4. 设置对象头(mark word)
// 5. 执行构造函数Q: OOM 的常见场景?
| OOM 类型 | 原因 | 解决方案 |
|---|---|---|
| Heap OOM | 对象创建速度超过回收速度 | 增大堆,调优 GC |
| Stack Overflow | 递归没有出口 | 检查递归逻辑 |
| Metaspace OOM | 动态生成大量类 | 增大 Metaspace |
Q: 为什么新生代要分 Eden 和 Survivor?
为了解决内存碎片和 GC 效率。如果直接在大块内存上复制存活对象,需要复制大量对象。用 Survivor 把 Minor GC 中"死不了但也没那么老"的对象先放一边,减少对象进入老年代的概率。
面试官考 JVM,不是考你背书,是考你理解不理解。
