一、知识结构及面试题目分析
JVM 进阶面试主要包括两类:
一是和工程实践结合的面试题,考察面试者是临时抱佛脚背了一些知识点还是真的有将 JVM 知识应用到平常的项目中:
- 解释线上的配置(如 gc 配置、gc 日志等等)
- 线上问题排查示例,涉及工具、经验、实例等;
- 和某些知识点结合的面试题。
二是 JVM 相关领域的新知识点。
二、典型面试例题及思路分析
问题 1:你们生产环境线上服务器的 JVM 启动参数配置是什么样的?
参考答案:
-Xms4096m -Xmx4096m -Xmn2048m -Xss512k -XX:MetaspaceSize=384m -XX:MaxMetaspaceSize=384m -XX:+UseConcMarkSweepGC -XX:+UseParNewGC -XX:CMSFullGCsBeforeCompaction=5 -XX:+UseCMSCompactAtFullCollection -XX:+DisableExplicitGC -verbose:gc -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -Xloggc:/home/admin/logs/gc.log -XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs
| JVM 参数 | 说明 |
|---|---|
| Xms | 初始堆大小 |
| Xmx | 最大堆大小 |
| Xmn | 年轻代大小 |
| Xss | 每个线程的堆栈大小 |
| MetaspaceSize | 首次触发 Full GC 的阈值,该值越大触发 Metaspace GC 的时机就越晚 |
| MaxMetaspaceSize | 设置 metaspace 区域的最大值 |
| +UseConcMarkSweepGC | 设置老年代的垃圾回收器为 CMS |
| +UseParNewGC | 设置年轻代的垃圾回收器为并行收集 |
| CMSFullGCsBeforeCompaction=5 | 设置进行 5 次 full gc(CMS)后进行内存压缩。由于并发收集器不对内存空间进行压缩 / 整理,所以运行一段时间以后会产生 "碎片",使得运行效率降低。此值设置运行多少次 full gc 以后对内存空间进行压缩 / 整理 |
| +UseCMSCompactAtFullCollection | 在 full gc 的时候对内存空间进行压缩,和 CMSFullGCsBeforeCompaction 配合使用 |
| +DisableExplicitGC | System.gc () 调用无效 |
| -verbose:gc | 显示每次 gc 事件的信息 |
| +PrintGCDetails | 开启详细 gc 日志模式 |
| +PrintGCTimeStamps | 将自 JVM 启动至今的时间戳添加到 gc 日志 |
| -Xloggc:/home/admin/logs/gc.log | 将 gc 日导输出到指定的 /home/admin/logs/gc.log |
| +HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs | 当堆内存空间溢出时输出堆的内存快照到指定的 /home/admin/logs |
点评:
这是一个典型的结合实践的问题,这类问题的答案并不唯一,相关参数也并不唯一(上面的参数只是笔者从服务器上随意摘取的例子),重点是要言之有物,即需要面试者真正关注过,在熟悉的基础上自由发挥。话又说回来,线上的参数配置有两类:一类是内存相关配置(如 Xms/Xms/Xmn),一类是 GC 相关配置(如 UseConcMarkSweepGC 等),完全的启动参数可以通过命令 java -XX:+PrintFlagsFinal -version 来查看。
追问:是否进行过 GC 优化?如何优化?
GC 优化的一般步骤:
1. 评估现状及设定目标。评估是否需要调优及调优的目标优先级。比如说降低 Full GC 的的执行时间,降低 Young GC 的执行时间等等;
2. 调优。根据 gc 日志等找到优化空间,比如说 Full GC 执行时间太长可能是因为老年代太大了,看能否调整为并行 GC 或者增加并行 GC 的线程数或者减少老年代大小等;
3. 评估效果。根据 gc 日志、jstat 等命令、Mat/Visual VM 等工具来监控调优效果;
4. 细微调整。 根据评估效果来进一步调整相关参数。
这个追题可以即是上个问题的延伸,本质上二者也是一类问题,即和实践结合的问题。这类问题的答案一般是:框架 + 实例。比如说这里的四个步骤就是 "框架",体现的是优化的系统化方案,即优化都跳不出这个圈子;但这个问题要答得出彩,最好附上优化实例,答案里并没有这部分,是因为这部分是需要候选人自己真正去动手实践过的,这样在回答时自己才会更有体感,也不怕进一步的追问。如果候选人没有相关的调优经历,可以去尝调整相关参数,比如说以降低 Young GC 时间次数为目的。
问题 2:介绍一下新的垃圾回收机制呢?
参考答案:
除了常规的 Serial/Parallel/CMS 等垃圾回收器外,目前比较新的垃圾回收器还有:
1、G1(Garbage First)GC,JDK7 开始引入,JDK9 以后的默认配置。
(1) 场景: 响应速度优先,面向服务端应用;
(2) 目标:尽量缩短处理超大堆(大于 4GB)时产生的停顿、解决 CMS 的内存碎片问题,替换 CMS;
(3) 特点:
A、分区收集,将整个 Java 堆划分为多个大小相等的独立区域(Region),虽然还保留新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,而都是一部分 Region(不需要连续)的集合;
B、可预测的停顿。根据各个 Region 里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的 Region。在回收时采用部分内存回收(在 YGC 时会回收所有新生代分区,在混合回收时会回收所有的新生代分区和部分老生代分区);
2、ZGC,JDK11 开始引入,目前尚处于实验阶段。
(1) 场景:解决 G1 GC 大内存支持不友好、内存利用率不高等问题;
(2) 目标:支持 TB 级内存、停顿时间控制在 10ms 之内、降低对整体应用性能的影响(对吞吐量的影响低于 15%)
(3) 特点:
A、不分代的垃圾回收器,即垃圾回收时对全量内存进行标记,以 page 为单位进行对象的分配和回收,但是回收时仅针对部分内存回收,优先回收垃圾比较多的 page。
B、当前只支持 Linux 的 64 位系统;
3、Shenandoah GC,JDK12 开始引入,也是一款实验性质的垃圾回收器。
(1) 目标: 最小化垃圾回收对用户代码造成的停顿(降至毫秒级)、支持 TB 级内存。
(2) 特点: 不分代的垃圾回收器。
点评:
这类题目偏向于问对 JVM 比较熟悉的候选人;主要考察候选人知识的广度,拓展性很大,答题要点不在于细节求多求全,而在于对知识点的准确了解,即知道这个点,且能准确说出若干个特性。
三、总结
JVM 的面试题的具有一定的区分度和灵活度,因此进阶题目的形式比较灵活多变,对于这类题目,整体上有两个方向:一是多和工程实践结合,比如说在测试环境的机器上调一下参数、观察一下日志、dump 一下内存进行分析,都是很好的学习手段,特别是带着问题去实践时,往往会很难忘;二是保持对新知识点的好奇。
四、扩展阅读及思考题
4.1 扩展阅读
4.2 思考题
- 说说令你印象比较深的 JVM 参数