JVM 中的垃圾回收算法及垃圾回收器详解

前言

Java 虚拟机(JVM)的自动内存管理机制中,垃圾回收(Garbage Collection, GC)是至关重要的一个环节。它不仅负责释放那些不再被使用的对象所占用的内存空间,还对程序的性能有着重要影响。本文将详细介绍 JVM 中的垃圾回收算法以及各个版本 JDK 对应的垃圾回收器,并探讨它们的工作原理和适用场景。


一、垃圾回收算法

1. 标记-清除(Mark-Sweep)

标记-清除是最基础的垃圾回收算法之一。其工作过程分为两个阶段:

  • 标记阶段:遍历所有对象引用,找出哪些对象是存活的。
  • 清除阶段:回收未被标记的对象所占用的空间。

优点

  • 实现简单,不需要额外空间。

缺点

  • 效率低,特别是在存在大量对象的情况下。
  • 容易产生内存碎片,导致后续大对象分配时出现空间不足的问题。

2. 复制(Copying)

复制算法将可用内存按容量划分为两块,每次只使用其中一块。当这一块内存用完时,就将存活的对象复制到另一块上面,然后把已使用的内存空间一次清理掉。

优点

  • 不会产生内存碎片。
  • 实现简单,运行高效。

缺点

  • 内存利用率低,因为需要预留一半的内存用于复制。

3. 标记-整理(Mark-Compact)

标记-整理结合了标记-清除和复制的优点。在标记阶段之后,不是直接清除未标记的对象,而是让所有存活的对象向一端移动,然后清理掉端边界以外的内存。

优点

  • 解决了内存碎片问题。
  • 提高了内存利用率。

缺点

  • 移动对象的成本较高。

4. 分代收集(Generational Collection)

现代垃圾回收器通常采用分代收集的思想,根据对象存活周期的不同将其划分成不同的区域进行回收。一般分为年轻代(Young Generation)、老年代(Old Generation)和永久代(Permanent Generation,现已改为元空间 Metaspace)。年轻代又细分为 Eden 区和 Survivor 区(S0/S1)。

  • 年轻代:新创建的对象首先分配在这里,由于大部分对象生命周期较短,所以这里采用复制算法。
  • 老年代:经过几次垃圾回收后仍然存活的对象会被晋升到这里,采用标记-清除或标记-整理算法。
  • 元空间:存储类的元数据等信息,从 JDK8 开始,永久代被移除,改用本地内存实现的元空间替代。

二、垃圾回收器

随着 JDK 版本的发展,JVM 引入了多种垃圾回收器,每种都有其特定的应用场景和优化方向。

1. Serial 收集器

Serial 收集器是最基本的新生代垃圾收集器,单线程执行,适用于单核处理器或小型应用。

  • JDK 版本:自 JDK1.3 以来一直存在。
  • 特点:简单高效,适合于客户端模式下的应用。

2. Parallel/Throughput 收集器

Parallel 收集器也是一款针对新生代的收集器,但它可以使用多线程并行执行,提高了垃圾回收效率,适合于多核服务器环境。

  • JDK 版本:从 JDK6 开始成为默认的服务器端收集器。
  • 特点:追求高吞吐量,减少 GC 停顿时间。

3. CMS(Concurrent Mark Sweep)收集器

CMS 是一种以获取最短回收停顿时间为目标的老年代垃圾收集器,通过并发方式执行大部分工作,减少了应用程序的停顿时间。

  • JDK 版本:JDK5 引入,JDK9 中被标记为废弃。
  • 特点:低延迟,但可能会导致碎片化问题,且 CPU 资源消耗较大。

4. G1(Garbage First)收集器

G1 是面向服务端应用的垃圾收集器,旨在替代 CMS。它将堆划分为多个大小相等的独立区域(Region),跟踪每个 Region 里垃圾的数量,优先回收价值最大的 Region。

  • JDK 版本:JDK7u4 作为实验性功能推出,JDK9 成为默认收集器。
  • 特点:预测停顿时间模型,可配置最大停顿时间目标;减少了 Full GC 的发生频率。

5. ZGC(Z Garbage Collector)

ZGC 是一个可扩展的低延迟垃圾收集器,设计目标是在任意堆大小下都能提供不超过 10 毫秒的暂停时间。

  • JDK 版本:JDK11 作为实验特性引入,JDK15 正式发布。
  • 特点:非常低的停顿时间,支持非常大的堆(TB 级别)。

6. Shenandoah

Shenandoah 与 ZGC 类似,也是一个低暂停时间的垃圾收集器,但它是由 Red Hat 开发并贡献给 OpenJDK 项目的。

  • JDK 版本:JDK12 引入,JDK15 GA。
  • 特点:同样专注于降低 GC 停顿时间,具有更广泛的平台支持。

三、总结

选择合适的垃圾回收器对于提升应用性能至关重要。不同的应用场景可能需要不同类型的垃圾回收策略。例如,对于响应时间敏感的服务端应用,可能更适合使用 G1 或 ZGC;而对于后台批处理任务,则可能更倾向于使用 Parallel 收集器来最大化吞吐量。

随着 JDK 版本的不断演进,新的垃圾回收技术也在持续发展。开发者应密切关注这些变化,并根据实际需求调整自己的垃圾回收策略,以获得最佳的应用性能。希望这篇文章能帮助你更好地理解 JVM 中的垃圾回收机制及其背后的原理。如果你有任何疑问或者想要分享的经验,请在评论区留言!

相关推荐
考虑考虑33 分钟前
JDK9中的dropWhile
java·后端·java ee
想躺平的咸鱼干41 分钟前
Volatile解决指令重排和单例模式
java·开发语言·单例模式·线程·并发编程
hqxstudying1 小时前
java依赖注入方法
java·spring·log4j·ioc·依赖
·云扬·1 小时前
【Java源码阅读系列37】深度解读Java BufferedReader 源码
java·开发语言
Bug退退退1232 小时前
RabbitMQ 高级特性之重试机制
java·分布式·spring·rabbitmq
小皮侠2 小时前
nginx的使用
java·运维·服务器·前端·git·nginx·github
Zz_waiting.3 小时前
Javaweb - 10.4 ServletConfig 和 ServletContext
java·开发语言·前端·servlet·servletconfig·servletcontext·域对象
全栈凯哥3 小时前
02.SpringBoot常用Utils工具类详解
java·spring boot·后端
兮动人3 小时前
获取终端外网IP地址
java·网络·网络协议·tcp/ip·获取终端外网ip地址
呆呆的小鳄鱼3 小时前
cin,cin.get()等异同点[面试题系列]
java·算法·面试