Java 后端面试里,JVM 和垃圾回收经常被问。很多候选人会背堆、栈、方法区、年轻代、老年代、垃圾回收算法,但一到项目追问就卡住:线上接口突然卡顿,你怎么判断是不是垃圾回收?内存一直涨,是内存泄漏还是流量上升?你看过哪些日志?参数为什么这样配?
这类问题真正考的不是记忆,而是排查能力。面试官想知道你能不能把 JVM 基础和线上现象连起来。
先把现象讲清楚
不要一上来就背参数。可以先讲现象:接口响应时间突然变长、服务偶尔无响应、内存曲线持续上涨、重启后恢复、某些批量任务后更明显。不同现象对应的怀疑方向不同。
如果是偶发卡顿,可以看垃圾回收暂停时间、线程状态、下游接口耗时;如果是内存持续上涨,可以看堆使用曲线、对象数量、缓存是否无限增长;如果是处理一批数据后变慢,可以看是否一次性加载太多对象。
垃圾回收日志要会翻译
面试里不一定要求你背所有日志字段,但要能说明关注什么。重点看回收频率、每次暂停时间、回收前后堆大小变化、是否频繁发生完整垃圾回收。完整垃圾回收一般代价更高,如果频繁出现,可能说明老年代压力大、对象晋升过快或内存设置不合理。
讲的时候尽量用中文解释,不要堆缩写。比如可以说“我会先看回收后内存有没有明显下降。如果每次回收后都降不下来,说明可能有对象一直被引用,不能被释放”。
内存泄漏要讲引用链
Java 也会有内存泄漏。常见原因不是对象没人回收,而是对象仍然被某个集合、缓存、线程本地变量或监听器引用着。面试里可以举例:把用户会话或查询结果放进静态集合,没有过期策略;本地缓存只增不删;线程池任务里保存了大对象;长连接对象没有释放。
定位时可以讲堆转储和对象引用链,也可以讲更朴素的路径:先看哪个接口或任务触发内存上涨,再缩小到具体模块,检查集合大小、缓存键数量、批处理数据量和对象生命周期。
参数调优不要脱离业务
很多人喜欢背垃圾回收器和参数,但参数不是万能答案。参数调优之前,要先确认是不是代码问题、数据量问题或缓存策略问题。把一个无限增长的缓存通过加大堆内存掩盖掉,只会延后事故。
更成熟的表达是:先定位对象来源和回收情况,再决定是否调整堆大小、年轻代比例、回收器或暂停目标。调完参数后要压测或灰度观察,而不是凭感觉上线。
可以这样回答:我不会直接说改某个 JVM 参数,而是先看接口耗时、内存曲线和垃圾回收日志,确认是否存在频繁回收或回收后内存降不下来。然后结合堆对象分析定位大对象来源。如果是缓存或集合无限增长,先改代码和过期策略;如果是正常流量带来的内存压力,再评估参数和机器资源。上线后观察暂停时间、堆使用率、完整垃圾回收次数和接口响应时间。
这样的回答比背参数更有说服力,因为它体现的是线上问题处理能力。
GC 排查不是直接调参数
JVM 题里最危险的回答是“先调大堆”。堆变大可能减少回收次数,也可能拉长停顿。真实排查要先看现象,再看日志,再判断对象增长原因。
| 现象 | 优先判断 | 证据 | 可能动作 |
|---|---|---|---|
| 接口周期性卡顿 | 是否与 GC 停顿重合 | GC 日志和请求耗时 | 优化对象分配或参数 |
| 内存持续上涨 | 泄漏还是缓存增长 | 堆 dump 和引用链 | 清理引用或限制缓存 |
| 老年代频繁回收 | 对象晋升过快 | 分代占用曲线 | 调整生命周期或容量 |
| Full GC 后不下降 | 存活对象过多 | dump 对象类型 | 查静态集合、监听器、缓存 |
面试里可以说:参数调优是最后一段,不是第一反应。先证明问题来自哪里,再改参数或代码,否则只是碰运气。