“ 请简述JDK8中常见垃圾收集器的工作原理及其不足?”
这是许多Java开发者在面试中经常遇到的经典问题。虽然JDK8引入了G1垃圾收集器,极大改善了内存管理,但依然存在停顿时间长、并发性能有限等瓶颈,影响了高并发场景下应用的响应速度。为了解决这些问题,JDK9和JDK10对GC机制进行了多项优化,特别是在并发回收和监控能力上有了重要突破。
本文将以JDK9和JDK10的新GC特性为切入点,结合Visual VM中新增的对象直方图功能,详解如何利用这些工具提升Java应用的性能表现与问题排查效率。
一、JDK9/JDK10 在 GC上的主要改进
🔸主要更新
🔸 JEP 214:统一日志格式(Unified JVM Logging)
- 引入统一的
-Xlog
命令行格式,可方便启用 GC、类加载、内存等模块的日志; - 替代 JDK8 中混乱的
-XX:+PrintGCDetails
等多个标志。
🔸 JEP 304:G1 并发 Full GC
- 在 JDK9 中,G1 的 Full GC 终于开始支持并发执行,缓解了 STW(Stop The World)时长过长的问题;
- 更适用于低延迟场景。
🔸 JEP 307:并行 GC 的线程局部分配缓冲(TLAB)优化
- 对 Parallel GC 模式的对象分配过程进行改进;
- 降低多线程争用,提高吞吐能力。
🚀 JDK10 新特性补充
- 引入实验性的 ZGC(低延迟 GC),不过正式可用还要到后续版本;
- G1 GC 中提升了对象分代晋升和 region 回收的效率。
✅ 与 JDK8 的主要差异
方面 | JDK8 | JDK9/JDK10 |
---|---|---|
GC 日志 | PrintGCXX 系列参数 | 统一的 -Xlog |
G1 Full GC | 单线程 STW | 支持并发 Full GC |
Survivor 分配 | 传统区分 Eden/S0/S1 | 增加直方图、动态阈值 |
Visual GC | 无对象年龄图 | 新增对象 Histogram 视图 |
二、JDK10 的垃圾回收总结
✅ JDK10 中仍然支持的主要 GC 类型
垃圾收集器 | 启用方式 | 简介 | 是否增强 | 适用场景 |
---|---|---|---|---|
Serial GC | -XX:+UseSerialGC | 单线程执行 GC,简单高效 | ✅ JEP 307 中优化了 TLAB | 小内存/单核系统 |
Parallel GC(吞吐量优先) | -XX:+UseParallelGC | 多线程的 YGC 和 FGC,老牌高吞吐 GC | ✅ TLAB 提升并发效率 | 服务端批处理,CPU 核心多 |
CMS(并发标记清除) | -XX:+UseConcMarkSweepGC | 老年代并发回收,降低停顿时间 | ❌ 计划废弃(已在 JDK9 标注 Deprecated) | 偏重响应时间的旧系统 |
G1 GC(默认) | -XX:+UseG1GC (默认) | 分 Region 管理内存,目标低延迟 | ✅ 支持并发 Full GC(JEP 304) | 服务端应用默认推荐 |
Epsilon GC(空 GC) | -XX:+UseEpsilonGC | JDK11 引入,仅用于性能测试 | ❌ 不在 JDK10 | 性能基准测试 |
ZGC | 实验性,JDK11 起可用 | 亚毫秒延迟的 GC | ❌ 不在 JDK10 | 超大堆/低延迟应用 |
🔍JDK10 中的垃圾收集器现状说明
📃两张图看懂垃圾收集器变更
JDK8中的垃圾收集器:
JDK10中的垃圾收集器:
🖊JDK10做了哪些改动
-
CMS 在 JDK9 中被标记为 Deprecated
-
JDK9 开始,CMS 已经不推荐使用,因为 G1 被认为是更现代的替代方案;
-
不过 JDK10 中仍能使用,直到 JDK14 正式移除。
-
-
Parallel GC 表现依然稳定
-
JDK10 中,JEP 307 对其 TLAB 做了线程局部优化,使其在多核服务器上表现更好;
-
在吞吐量为主(如日志处理、大批量数据计算)的应用中仍很有市场。
-
-
Serial GC 适用于容器、小型应用
- 在容器内(低 CPU 限制)运行的小型 Java 服务中,Serial GC 依然简洁高效。
✳️ 如何选择垃圾收集器(JDK10)
场景 | 推荐 | GC原因 |
---|---|---|
响应时间敏感、低延迟 | G1 GC | 默认、支持并发 Full GC |
吞吐量优先、大数据计算 | Parallel GC | 多线程,GC 停顿集中但快速 |
单线程、小内存场景 | Serial GC | 开销小,适合嵌入式或小容器 |
兼容旧系统 | CMS(非推荐) | 若未切换到 G1,可继续使用 |
三、VisualVM
查看对象直方图
步骤一:安装 Visual GC 插件
- 打开
VisualVM
→ 工具 → 插件 → 可用插件 → 勾选 Visual GC → 安装。
步骤二:连接目标 Java 进程
- 在左侧找到 Java 应用,右键“Visual GC”;
步骤三:观察 Histogram 面板
- 可实时查看不同年龄对象的内存占用趋势;
- 横轴为对象年龄,纵轴为 Survivor 中内存占用;
步骤四:结合 GC 日志验证
建议搭配如下 JVM 参数一起使用:
-XX:+PrintGCDetails -XX:+PrintTenuringDistribution
高阶技巧:
- 点击直方图柱子不可操作,但结合 Monitor 面板中 Eden/Old 使用情况可以更准确判断对象流向;
- 多次采样或长时间监控,可发现是否存在内存泄漏或 Survivor 压力过大现象。
四、对象直方图 Histogram 功能
🔍 1. 什么是对象 Histogram?
Visual GC 插件中新增了对 Survivor 区中对象“年龄”的可视化直方图展示。每一列表示某个年龄层(即在 Survivor 区中经过的 GC 次数)的对象数量或占用内存。
📌 2. 核心参数解释
参数 | 含义 |
---|---|
Tenuring Threshold | 对象最多在 Survivor 区经历多少次 GC 后晋升到老年代(默认 15) |
Max Tenuring Threshold | JVM 允许的最大值(也是 15) |
Desired Survivor Size | Survivor 区理想大小(如 47MB) |
Current Survivor Size | 当前真实使用大小(单位:KB/MB) |
✅ 示例截图解析
我们截一个启动中的jdk10应用的visual GC来看下:
在上图中,我们观察到:
- Histogram 图中对象从年龄 1 到 6 有明显占用;
- 年龄 7-15 全为空,说明很多对象未等老化晋升即已消失或已被提前晋升;
- 当前 Survivor 区仅使用了 8KB,远小于期望值,表明 Survivor 区空间使用严重不足。
🔧 排查建议
观察现象 | 可能问题 | 建议调整 |
---|---|---|
柱子集中在 1~2,后续为空 | Survivor 区太小,无法维持多次 GC 生命周期 | 增大 Survivor 区,如 -XX:SurvivorRatio=6 |
Histogram 快速震荡、频繁变化 | 有大量中等生命周期对象频繁移动 | 优化对象生命周期,避免短命对象进入 Survivor |
Old 区增长迅速但 Survivor 未满 | 晋升阈值太低或晋升过早 | 增加 -XX:MaxTenuringThreshold=15 |
五、总结
JDK9及JDK10引入的并发垃圾回收改进,有效缩短了应用停顿(STW)的时间,提升了整体系统响应速度,显著增强了用户体验。同时,Visual VM中新增加的对象直方图功能,为开发者提供了更直观的对象生命周期视图,帮助精准定位内存热点和潜在的内存泄漏问题。通过结合并发GC的性能提升与直方图的细粒度分析能力,我们不仅能够加速应用运行效率,还能更高效地进行问题排查和内存优化,保障Java应用的稳定性和持续健康发展。