JVM垃圾收集器

本文介绍了JVM里的垃圾收集器,包括Serial、ParNew、Parallel类型、CMS和G1收集器。阐述了各收集器特点、适用场景及搭配方式,还指出CMS存在对资源敏感、无法收集浮动垃圾、回收后空间有碎片等缺陷,而G1能统一新生代和老年代回收方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

前面的文章描述了标记算法和回收算法,那么这次的文章我们来聊一下,目前JVM里面使用的垃圾收集器吧,以及通过这些收集器之间的关系,顺便也看看目前最新的收集器G1的相关内容,以及介绍之间的关系来帮助大家后续更好的在实际中调优自己的JVM,本次文章内容参考书籍:
《Java虚拟机-JVM高级特性与最佳实践》

首先让我们看看收集器和收集器之间的关系:
在这里插入图片描述 图中可以看到,除了G1以外,所有的收集器是可以相互搭配使用的,搭配的不只是新生代和老年代,也有同代之间的搭配例如:CMS 和 Serial Old。此外也发现收集器并不是适合所有的分代的,例如ParNew 只能使用在年轻代,而无法使用在老年代。

Serial 收集器

Serial收集器应该是JVM最古老的收集器了,在JDK 1.3之前,它几乎是新生代收集的唯一选择。从名字上可以看出Serial 本身就是串行单线程的,在执行收集工作的时候收集器会触发Stop The World (用全世界都停止,来比喻JVM暂停业务来进行垃圾回收) 来暂停所有的用户进程,从而导致JVM无法响应外界请求。

Serial 收集器是有两种的,一个是Serial 另一个是 Serial Old,这两种的区别,一个是使用在新生代采用复制算法,另一个是使用在老年代采用整理算法。一般Serial 是和 CMS (CMS的介绍在后面) 搭配使用的,当然Serial Old 也可以和 ParNew 搭配使用(后面介绍),我们先看看Serial 收集器的的执行流程
Serial 搭配 Serial Old
从图中可以看出来,无论是新生代还是老年代都是使用单线程的方式进行收集,这么做有好有坏,好处是线程能更加集中精力收集,但是也因为是单线程导致基于JVM的系统可用性降低,但是Serial回收后的空间一般都是规整的,没有过度的碎片化,说白了Serial就是使用最粗暴的方式来回收-整理内存的

ParNew 收集器

在CMS收集器还没出来之前,ParNew自诞生后都是和Serial Old配合使用的,也由于Serial是单线程的方式,回收效率低下,对于新生代这种回收特别频繁的分代来说无疑是一场噩梦,所以才诞生了ParNew收集器,目的就是为了使用在需要频繁回收的分代里面 (例如年轻代) 来加快回收效率,从而降低对用户的影响。
ParNew搭配Serial Old

Parallel 类型收集器

Parallel 是后续出现的一种垃圾收集器,虽然使用的较少,但是其特性还是有使用场景的,Parallel最大的特点就是可以通过指定的吞吐量自动调整新生代需要回收堆空间的大小,从而控制垃圾回收的执行时间。
吞 吐 量 = 运 行 用 户 代 码 的 时 间 运 行 用 户 代 码 的 时 间 + 垃 圾 收 集 的 时 间 吞吐量 = \frac{运行用户代码的时间}{运行用户代码的时间 + 垃圾收集的时间} =+
在这里插入图片描述
在这里插入图片描述
单是这里也会衍生出一个问题,如果将吞吐量调高,那么用户响应自然就高了,但是同时年轻代的回收次数也变高了,年轻代进入老年代的数度也变快的,这样不但整体的吞吐量没有上升,反而会给老年代增加负担。

这里为什么要说是Parallel类型呢?因为和Serial 一样 Parallel分为两种:
1、Parallel Scavenge (用在年轻代可以和Serial Old 或者 Parallel Old 搭配使用)
2、Parallel Old(用在老年代,目的是为了优化和Serial Old搭配使用导致的性能问题)

CMS 收集器

CMS诞生之初的目标就是希望压缩服务停顿时间,加快响应数度,所以大多互联网企业B/S架构的尤其青睐这个收集器,这也就是为什么CMS会经常被人们认为是最好的收集器。CMS允许用户线程和GC收集线程并发执行,从而避免Stop The World带来的影响。
CMS整体分为4个步骤:

1.初始标记
2.并发标记
3.重新标记
4.并发清除

其中:
第一步的并发标记,目的主要是找到GC Root 能之间关联到的对象,虽然这步也会Stop The World但是执行时间是非常短暂的,对于外界其实是无感知的。
第二步的并发标记就是根据GC Root去标记需要回收的对象 ,为并发清除做好准备
第三步由于并发标记是和用户线程交替执行的难免标记的时间会被拉长,必然会出现一个情况,就是当标记期间又有需要被清理的对象出来了,那么第三步会基于第二步的基础上再进行一次标记,通常来说,这段时间也是非常短暂的,会远远小于并发标记。
耗时关系:(初始标记 < 重新标记 << 并发标记)
第四步就是并发执行回收工作了,以及最终的重置线程,将GC线程恢复成用户的线程
CMS

CMS 不是万能的解决方案

从上面众多的收集器来看,视乎CMS以及是一个出神入化的收集器了,而且CMS搭配ParNew进行并发收集基本上可以完美的应付各种场景,但是CMS并不是万能的,它也有缺陷,正由于它的特性才导致了它有特别的缺陷:

第一个、CMS 对资源特别敏感
第二个、CMS 无法收集浮动的垃圾
第三个、CMS 回收后的空间会有大量的碎片空间

第一个缺陷:在进行并发标记和并发清除的时候,CMS会分配将近一半的性能给GC线程(可以调整),如果性能分配的越多,并发执行的速度越快,但是分配给用户线程的资源也会越少。同理性能分配的越少,并发执行时间也就越长,资源占用时间也会越长,所以这里就有一个调优的过程

第二个缺陷:在执行并发标记和并发清除的时候,由于时间被拉长,必然会导致期间又会出现新的需要回收的对象,时间拉的越长这种情况越严重 (就好像你妈妈在房间打扫卫生,你却在房间不停的扔纸屑,这样永远打扫不完-官方比喻)

第三个缺陷:由于在并发清除的时候为了保证和用户和GC线程能交替执行,所以采用标记-清除算法,这么做的一个最大的缺陷就是空间碎片化会很严重,虽然官方提供了一个参数

	+UseCMSCompactAtFullCollection //是否使用Full GC进行碎片整理(默认开启)
	--XX:CMSFullGCsBeforeCompaction // 当CMS回收多少次后进行碎片整理(默认值为0)

这么做的方式虽然可以治理碎片化的问题,但是也意味着Full GC期间必然会造成Stop The World

除这些之外还有一个问题,就是当使用CMS的时候如果,如果发生了Concurrent Mode Failure会导致当前不再使用CMS而装换为Serial Old的方式进行收集,这无疑是一种致命的硬伤,所以为什么大家那么害怕Full GC的原因了,因为问题的来源经常是多样的。

G1 收集器

前面介绍了那么多收集器,貌似都是为了某一个特定的场景,或者特点的分代使用的,但是到目前为止还没有一个是能同时适用于年轻代,又同时适用于老年代的收集器。G1收集器就是这么诞生的。
G1的全名叫做(Garbage-First),诞生于JDK6,商业化于JDK7,从JDK6立项之初,它的目的就是为了替换掉CMS,同时也将新生代和老年代的回收方式做了一个统一
G1的特点有以下几点:

1、支持并行于并发
2、分代收集
3、空间整合
4、可预测停顿

G1
由于G1收集器把内存化整为零,通过把Java堆划分成多个Region管理,虽然堆中仍然存在分代的思想,但是Region其实也弱化了分代。PraNew + CMS 由于 内存空间的扩展会导致ParNew的回收效率越来越低,但是G1对于新生代回收的效率并不会因为,新生代的空间变大而变小(当然有一个临界值,当大于者临界值G1的效果好于ParNew,否则,新生代的回收效率G1和ParNew相差是不远的)。

未完待续…

### JVM垃圾收集器的工作原理 JVM中的垃圾收集器负责自动管理释放不再使用的内存资源。这一过程对于维护Java应用程序的性能至关重要[^1]。垃圾收集的主要目标是在不影响应用正常运行的前提下尽可能高效地回收无用对象所占有的空间。 #### 垃圾收集器类型及其特性 多种类型的垃圾收集器存在于现代版本的JVM中,每种都有各自的设计理念来适应特定应用场景下的需求: - **Serial GC**:适用于单核处理器的小规模应用环境,在年轻代采用复制算法,在年代则使用标记-整理算法。 - **Parallel GC (也称为Throughput Collector)**:专为多CPU系统设计,旨在最大化吞吐量,即完成更多有用工作的比例相对于总执行时间而言。该收集器同样区分新生代与年代,并分别运用不同的清理策略以达到最佳效果。 - **CMS (Concurrent Mark-Sweep) GC**:专注于降低暂停时间而非整体效率,适合于那些对响应速度敏感的服务端程序。它可以在后台逐步扫描存活对象并清除死亡对象而不必完全停止整个应用程序进程。 - **G1 (Garbage First) GC**:自JDK 7更新版引入以来成为默认选项之一,特别擅长处理具有大量活跃数据的大容量堆配置。G1将整个堆划分为多个固定大小的区域(region),并通过预测哪些地区最有可能包含可回收的空间来进行优先级排序。 - **ZGC Shenandoah GC**:这两种新型低延迟垃圾收集器是从JDK 11开始加入的支持超大型堆(可达数TB级别)的同时具备亚毫秒级别的短暂停滞特性的工具[ZGC][^5]。它们都采用了先进的并发技术使得大部分垃圾回收活动能够在不停止用户线程的情况下发生。 ### 如何选择合适的垃圾收集器? 选择最适合项目需求的垃圾收集器取决于具体的应用场景以及期望达成的目标。如果追求最高的吞吐率,则可能倾向于使用`Parallel GC`; 若更看重快速反应时间较低的停顿频率,那么像`CMS`, `G1`, 或者最新的`ZGC/Shenandoah`可能是更好的选择。值得注意的是,“最优”的方案并非永恒不变——随着业务逻辑的发展技术进步,原先选定的最佳实践可能会变得不合适,因此定期审查当前设置总是明智之举[^2]。 ### 性能调优建议 为了使选中的垃圾收集器发挥最大效能,可以通过调整一系列参数来进行精细化控制。这包括但不限于设定初始/最大堆尺寸(-Xms/-Xmx), 新生代占比(-XX:NewRatio), 生存阈值(-XX:+UseAdaptiveSizePolicy,-XX:MaxTenuringThreshold)等。此外,启用详细的日志记录功能可以帮助诊断潜在瓶颈所在之处,从而指导后续改进措施的方向。最终目的是找到一个平衡点,在满足服务等级协议(SLA)关于响应时间吞吐量的要求之间取得良好折衷。 ```bash java -Xms512m -Xmx4g -XX:+UseG1GC MyApplication ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值