JVM垃圾收集器

前言

前面的文章描述了标记算法和回收算法,那么这次的文章我们来聊一下,目前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相差是不远的)。

未完待续…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值