2051 字
约 6 分钟
8
S25-J08 · JVM 调优题库:收集器选型、诊断四件套与三套实战

适用读者:准备中高级 Java 面试、要在生产上处理 GC 抖动或内存泄漏的人。
前置:S04(JVM 基础)、S25-J07(并发题库)、J05(原理速查)。
覆盖旧文:2776/2777/2780 + 3036–3053(JVM 调优教程 18 章)→ 18 篇 / 约 10 万字符。
扩展点:E-S25-024(收集器选型决策表)、E-S25-025(诊断四件套与"先现象后原因")、E-S25-026(版本差异:JDK 8/17/21 默认收集器)。
上一篇:S25-J07 · 并发题库讲评:从 JUC 二十章到线程池十四问
下一篇:题库层第 3 篇 · MySQL 题库讲评(待写)。


1. 一句话结论

JVM 调优不是"调参数",是"用数据定位瓶颈,再决定要不要动 JVM"。 面试的标准答法是:现象 → 指标 → 工具 → 根因 → 验证;只说"加大堆内存"是零分回答。

出处:JVM 调优教程 18 章(A-3036–A-3053)+ A-2777/A-2780 同题重文。


2. 收集器选型决策表(E-S25-024)

收集器 分代 线程 算法 目标 适用
Serial 新生代 单线程 STW 复制 简单 客户端、几十~一两百 MB 堆、单核
Parallel Scavenge / Old 分代 多线程 STW 复制 / 标记-整理 吞吐量优先 批处理、后台运算、不敏感停顿
CMS 老年代 并发 标记-清除 低延迟 历史选型(JDK 14 移除)
G1 整堆(Region) 并发 复制+标记-整理 可预测停顿 大堆(>4 GB)默认选择
ZGC 整堆 并发 染色指针+读屏障 超低停顿(<1 ms 级) 超大堆、极低延迟要求
Shenandoah 整堆 并发 Brooks 转发指针 低停顿 非 Oracle JDK 生态常见

版本边界(E-S25-026,必答且常被追问):

JDK 默认 GC 备注
8 Parallel(Server 模式) 需手动 -XX:+UseG1GC 才用 G1
9–10 G1 JEP 248 起默认
11 G1 ZGC 实验性(JEP 333)
14 G1 CMS 移除(JEP 363)
15 G1 偏向锁默认禁用(JEP 374)
17 G1 ZGC 生产可用(JEP 377)
21 G1 分代 ZGC(JEP 439)、虚拟线程(JEP 444)

判据:先问"你的 JDK 是几",再谈默认收集器。 答"默认 Parallel"在 JDK 11 上会被直接判错。


3. 诊断四件套与"先现象后原因"(E-S25-025)

top -Hp <pid>                 # 1. 哪个线程吃 CPU(拿 nid)
printf "%x\n" <nid>           # 2. 转 16 进制
jstack <pid> > dump.txt       # 3. 抓栈,grep -A20 <nid>
jstat -gcutil <pid> 1000      # 4. GC 频率与各代占用
jmap -histo:live <pid> | head # 5. 对象直方图(看谁在涨)
jcmd <pid> VM.flags           # 6. 实际生效参数(别信启动脚本)

诊断六原则(原文第 17 章):先整体后局部 / 先现象后原因 / 先简单后复杂 / 数据驱动 / 可重现验证 / 总结经验。

3.1 四类问题的现场手法

现象 首要怀疑 手法
CPU 高 死循环、正则回溯、频繁 GC、锁自旋 top -Hp → jstack → 找热点方法;arthas profiler
响应慢 GC 停顿、线程阻塞、IO 等待、慢 SQL jstat -gc 看停顿;jstack 看 BLOCKED/WAITING;APM 链路
内存涨 泄漏(静态集合、ThreadLocal、连接未关) jmap -histo:live 两次对比;jcmd GC.heap_dump 出 dump 用 MAT
OOM 堆 / 元空间 / 直接内存 / 线程栈 看错误文本:Java heap space / Metaspace / Direct buffer memory / unable to create native thread

判据:OOM 的四种类型对应四种不同根因,答"加大 -Xmx"只在第一种情况成立。


4. 三套实战:按版本与场景分

4.1 JDK 8:Parallel 到 G1 的迁移评估

# 老环境(JDK 8 + Parallel,堆 8G)
java -Xms8g -Xmx8g -XX:+UseParallelGC -XX:+PrintGCDetails -XX:+PrintGCDateStamps \
     -Xloggc:/var/log/app/gc.log -jar app.jar
# 评估:单次 Full GC 是否 > 1s、老年代晋升速率

迁移判据:堆 > 4 GB、Full GC 停顿 > 500 ms、且应用对尾延迟敏感 → 试 G1;改 -XX:+UseG1GC -XX:MaxGCPauseMillis=200 后对比 p99 而非均值。

4.2 JDK 17:G1 参数的最小集

java -Xms4g -Xmx4g -XX:+UseG1GC -XX:MaxGCPauseMillis=200 \
     -XX:G1HeapRegionSize=8m -XX:+ParallelRefProcEnabled \
     -Xlog:gc*:file=/var/log/app/gc.log:time,uptime,level,tags -jar app.jar

要点:-Xms=-Xmx 避免堆伸缩;MaxGCPauseMillis 是软目标(G1 会尽力,不保证);-Xlog 是新版统一日志,旧的 PrintGCDetails 在 JDK 9+ 已不生效。

4.3 JDK 21:分代 ZGC 与虚拟线程

# 极低延迟(金融/网关),大堆
java -Xms16g -Xmx16g -XX:+UseZGC -XX:+ZGenerational \
     -Xlog:gc*:file=/var/log/app/zgc.log -jar app.jar

边界:ZGC 的代价是吞吐略低 + 内存占用更高(染色指针/转发表);虚拟线程适合 IO 密集,但 synchronized 块会 pin 住载体线程(JDK 21),密集锁场景仍应评估平台线程。


5. 高频题速查:答准而不是答多

题 关键点
内存结构 堆、方法区/元空间、虚拟机栈、本地方法栈、程序计数器、直接内存(不在 JVM 规范内)
对象一定在堆上吗 逃逸分析后可栈上分配/标量替换(是优化不是保证)
什么时候进老年代 年龄达阈值、大对象直接进、动态年龄判定(Survivor 同龄和 > 一半)
Full GC 触发条件 老年代不足、元空间不足、System.gc()(受 -XX:+DisableExplicitGC 影响)、G1 的并发模式失败
类加载双亲委派 防核心类被篡改;打破场景:SPI(Thread.contextClassLoader)、OSGi、热部署
JIT 什么时候生效 解释执行 → C1 → C2;-XX:+PrintCompilation 观察;峰值性能要预热
怎么判断内存泄漏 老年代占用持续上升且 Full GC 后不回落 + jmap -histo:live 两次对比

6. 监控体系:分层而不是堆工具

层 指标 工具
基础 CPU/内存/磁盘/网络 node_exporter
JVM 堆/非堆、GC 次数与停顿、线程数、类加载 jmx_exporter + /actuator/prometheus
应用 RT、QPS、错误率 Micrometer / SkyWalking
业务 关键流程成功率 自定义埋点
# prometheus.yml 采集 JVM 指标(Spring Boot 3 + Actuator)
scrape_configs:
  - job_name: 'jvm-app'
    metrics_path: '/actuator/prometheus'
    static_configs:
      - targets: ['localhost:8080']

要点:heap_used 要配 jvm_gc_pause_seconds_max 一起看——只看堆占用会误判(G1 下堆高位但停顿正常是健康状态)。


7. 反例:四种"看起来专业"的错答

答法 问题
"调优就是把 Xmx 调到物理内存 80%" 无前提;容器里还要看 cgroup limit
"G1 一定比 Parallel 快" 吞吐通常不如 Parallel;选型看目标(延迟 vs 吞吐)
"出现 OOM 就加堆" 元空间/直接内存/线程栈三类 OOM 加堆无效
"CMS 是最佳低延迟方案" JDK 14 已移除;应说 G1/ZGC

8. 自测

  1. JDK 8 / 11 / 17 / 21 的默认收集器分别是什么?
  2. CPU 飙高的六步定位流程?
  3. 四种 OOM 的错误文本与根因?
  4. 为什么 -Xlog:gc* 取代了 -XX:+PrintGCDetails?
答案
  1. Parallel / G1 / G1 / G1(JDK 21 另有分代 ZGC 可选)。
  2. top -Hp → printf %x → jstack → grep -A20 nid → 定位方法 → 结合 jstat 排除 GC。
  3. Java heap space(堆)、Metaspace(元空间)、Direct buffer memory(直接内存)、unable to create native thread(线程/栈)。
  4. JDK 9 起统一日志框架(JEP 158),旧参数失效或被映射,粒度控制更细(tags/level/output)。

9. 下一篇

题库层第 3 篇 · MySQL 题库讲评 —— 索引与 B+ 树、事务与 MVCC、锁升级与临键锁、redo/undo 与两阶段提交、主从延迟,以及"分库分表后分页怎么查"这类场景题的标准答法。


本篇属于系列《S25 · 求职与面试》。旧文来源:JVM 调优教程 18 章(A-3036…3053)+ A-2777/2780;扩展点 E-S25-024/025/026。技术细节点以 duplicate_ref 指回 S04/S11 主讲系列;本文只给答题骨架与边界。

S25-J08 · JVM 调优题库:收集器选型、诊断四件套与三套实战
https://www.clxhxhhr.top/posts/5589/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。