HelloCoder HelloCoder
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
  • 《k8s学习心得》
随笔
关于作者
首页
《Java小白求职之路》
《小白学Java》
计算机毕设
  • 一些免费计算机资源
  • 脚手架工具
  • 《从0到1学习Java多线程》
  • 《从0到1搭建服务器》
  • 《可观测和监控》
  • 《k8s学习心得》
随笔
关于作者
  • 《PureJavaCoderRoad》

    • 导读

    • Java基础

    • Java进阶

    • Java高阶

      • JVM

        • G1、ZGC有了解过吗?
        • JVM掌握的知识点
        • Java内存区域
        • Java内存模型
        • Java内存模型的happen-before
        • 类加载机制
        • 聊聊你熟悉的垃圾回收器
        • 谈谈Java的垃圾回收
        • 谈谈创建对象的过程
      • Java线程

      • 数据结构与算法

    • 开发辅助工具

    • 计算机网络

    • 数据库

    • JavaEE

    • 中间件

    • 架构

    • 建议

  • PureJavaCoderRoad
  • Java高阶
  • JVM
#垃圾回收器
HaC
2022-05-26
目录

聊聊你熟悉的垃圾回收器

# 你一般使用什么 JDK 版本?默认的垃圾回收器是什么

  • JDK 11:现在大多数公司已经稳定运行在 11 上,生态成熟,Spring Boot 2.x 对其支持很好。
  • JDK 17:随着 Spring Boot 3.x 和 Spring Cloud 的普及,17 已经是一个非常好的选择。它带来了更强的 GC 能力、更好的容器感知,并且比 8 的性能有明显提升。
  • JDK 8:虽然很多老系统还在用,但目前新项目已不太适合作为起点。如果现在还在选 8,可能会在未来 1-2 年遇到 Spring 新版本不支持的问题。
JDK 版本 默认垃圾回收器 (Default GC) 备注
JDK 7 (及更早) Parallel GC 在JDK 7u4版本后,老年代也使用了Parallel Old GC 。
JDK 8 Parallel GC 延续了JDK 7后期的默认选择,是许多线上系统的常见版本。
JDK 9 及以上 G1 GC (Garbage-First) 从JDK 9开始成为默认,并持续作为后续LTS版本(如JDK 11, 17, 21)的默认GC 。
JDK11 ZGC 引入了分代ZGC (Generational ZGC),进一步优化了性能和内存开销。

# 使用什么垃圾回收器?

这个要看具体场景,不能一概而论:

场景 推荐 GC 理由
低延迟服务(在线业务) G1GC 可预测停顿时间,默认自适应,适合大多数互联网应用
高吞吐量(离线计算、批处理) Parallel GC 吞吐量优先,GC 停顿时间可以稍长
超低延迟(金融交易) ZGC / Shenandoah 停顿时间 < 1ms,JDK 17 已经非常成熟
大堆内存(> 32GB) ZGC G1 在大堆下 Full GC 风险增大,ZGC 更稳定

目前线上我一般用 G1GC(-XX:+UseG1GC),然后根据监控调整 MaxGCPauseMillis,默认 200ms,通常调到 100ms 左右。到了 JDK 17,如果堆内存超过 32GB,我会切换到 ZGC。

# 常用常见的JVM参数有哪些?

-Xms4g -Xmx4g 
-XX:+UseG1GC 
-XX:MaxGCPauseMillis=100 
-XX:+HeapDumpOnOutOfMemoryError 
-XX:HeapDumpPath=/path/to/dump 
-XX:+PrintGCDetails 
-XX:+PrintGCDateStamps 
-Xloggc:/path/to/gc.log
参数 作用
-Xms / -Xmx 堆内存初始 / 最大值
-XX:MaxGCPauseMillis G1 的目标停顿时间
-XX:+HeapDumpOnOutOfMemoryError OOM 时自动 dump 内存
-XX:+DisableExplicitGC 禁止显式 System.gc()(防止框架误触 Full GC)
-Xloggc GC 日志路径(JDK 9+ 用 -Xlog:gc*)

# 为什么堆内存的初始大小(-Xms)和最大值(-Xmx)要设置一样?

  • 避免扩容开销:JVM 在堆内存不足时会触发扩容(从初始到最大值),这个过程涉及重新分配内存、调整 GC 阈值,属于 Stop-The-World 操作。直接设成一样,一开始就把内存占好,省去扩容这一步。
  • 避免弹性回收:如果初始小、最大大,JVM 在低负载时会缩容,高负载时再扩容。这个过程中的内存调整本身会造成性能抖动。固定堆大小可以让性能曲线更平稳,对在线业务更友好。
  • 容器环境更必要:在 K8s 里,容器内存限制是固定的,-Xms 和 -Xmx 保持一致可以避免 JVM 反复申请、释放内存,减少被 cgroup 驱逐的风险。

如果有特殊情况需要 Xms < Xmx(比如多应用混部、内存敏感的场景),也可以,但通常不推荐。对于大多数在线服务,最稳定的配置就是 -Xms = -Xmx。

# 分区、分代是如何工作的

分代回收的理论基础是“弱分代假说”,即绝大多数对象存活时间很短。基于此,JVM将堆内存划分为新生代(Young Generation) 和老年代(Old Generation),并对它们采用不同的回收算法和频率。

# 新生代回收 (Minor GC / Young GC)

  • 触发时机:Eden区空间不足时。
  • 回收算法:复制算法。
  • 回收过程:
    1. Eden区存活的对象 + 当前Survivor区(S0)存活的对象,被复制到另一个空闲的Survivor区(S1)。
    2. 复制过程中,存活对象的年龄会+1。
    3. 清空Eden和S0区。
    4. 年龄达到晋升阈值(默认15岁)的对象,会被晋升(Promotion) 到老年代。
  • 特点:频率高,速度快,但会暂停所有应用线程(STW,Stop-The-World)。

# 老年代回收 (Major GC / Full GC)

  • 触发时机:老年代空间不足、晋升失败、或System.gc()被显式调用时。
  • 回收算法:标记-清除(Mark-Sweep) 或 标记-整理(Mark-Compact)。
  • 回收过程:
    1. 标记:标记所有存活的对象。
    2. 清除:回收未被标记的对象(标记-清除);或将所有存活对象向一端移动,然后清理边界外的内存(标记-整理)。
  • 特点:频率低,但耗时较长,通常也是STW。

总的来说:

  • 分代是策略,目的是根据对象生命周期来优化回收效率。所有主流回收器都遵循这一策略。
  • 分区是实现,是对堆内存的具体划分方式。传统回收器如Parallel GC采用物理分区,而G1/ZGC等现代回收器采用逻辑分区(Region)。
  • 回收过程:无论哪种回收器,核心都是 “标记存活对象 → 回收死亡对象 → 整理/复制存活对象” 的过程。区别在于:
    • Parallel GC 追求高吞吐量,但可能带来较长的停顿。
    • G1 GC 追求可控的低停顿,通过增量回收和优先级回收来实现。
#垃圾回收器
上次更新: 2026-06-29 17:10:35
最近更新
01
脏读、幻读
06-29
02
G1、ZGC有了解过吗?
06-29
03
常见的存储系统
06-29
更多文章>
Theme by Vdoing | Copyright © 2020-2026 HaC
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式