JVM系列十一——垃圾收集器

一、介绍

垃圾收集器是垃圾回收算法(标记-清除算法、复制算法、标记-整理算法、火车算法)的具体实现,不同种类JVM所提供的垃圾收集器可能会有很大差别,HotSpot虚拟机中的8种垃圾收集器:Serial、ParNew、Parallel Scavenge、Serial Old、Parallel Old、CMS、G1、ZGC
在这里插入图片描述
垃圾收集器是分代的:

  • 年轻代:见图
  • 年老代:见图
  • 通杀:G1

并行(Parallel)
指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态;如ParNew、Parallel Scavenge、Parallel Old;
并发(Concurrent)
指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行)用户程序在继续运行,而垃圾收集程序线程运行于另一个CPU上;

二、串行收集器

2.1 Serial收集器

Serial(串行)垃圾收集器是最基本、发展历史最悠久的收集器;JDK1.3.1前是HotSpot新生代收集的唯一选择;

特点:
采用复制算法,单线程收集,Stop The World 进行垃圾收集时,必须暂停所有工作线程,直到完成;

Serial/Serial Old组合收集器运行示意图如下:
在这里插入图片描述
应用场景
依然是HotSpot在Client模式下默认的新生代收集器;

也有优于其他收集器的地方:

  • 简单高效(与其他收集器的单线程相比);
  • 对单个CPU环境,Serial收集器没有线程切换开销,可获得最高的单线程收集效率;
  • 在用户的桌面应用场景中,可用内存一般不大(几十M至一两百M),可以在较短时间内完成垃圾收集(几十MS至一百多MS),只要不频繁发生,这是可以接受的。

2.2 Serial Old收集器

Serial Old是 Serial收集器的老年代版本;

特点
针对老年代;
采用"标记-整理"算法(还有压缩,Mark-Sweep-Compact);
单线程收集;

三、并行收集器

3.1 ParNew收集器

ParNew垃圾收集器是Serial收集器的多线程版本。

特点
除了多线程外,其余的行为、特点和Serial收集器一样;
与Serial收集器一样,可用控制参数、收集算法、Stop The World、内存分配规则、回收策略等,两个收集器共用了不少代码;
在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销。

ParNew/Serial Old组合收集器运行示意图如下:
在这里插入图片描述

3.1.1 为什么只有ParNew能与CMS收集器配合

1.CMS是HotSpot在JDK1.5第一款并发收集器,让垃圾收集线程与用户线程(基本上)同时工作;
2.CMS作为老年代收集器,但无法与JDK1.4新生代收集器Parallel Scavenge配合工作;
3.因为Parallel Scavenge(以及G1)都没有使用传统的GC收集器代码框架,而另外独立实现;而其余几种收集器则共用了部分的框架代码;

3.2 Parallel Scavenge收集器

因为与吞吐量关系密切,也称为吞吐量收集器(Throughput Collector)。

特点
有一些特点与ParNew收集器相似

  • 新生代收集器
  • 采用复制算法
  • 多线程收集
  • Parallel Scavenge收集器的目标则是达一个可控制的吞吐量(Throughput)

3.3 Parallel Old收集器

Parallel Old垃圾收集器是Parallel Scavenge收集器的老年代版本;JDK1.6中才开始提供;

特点

  • 针对老年代;
  • 采用"标记-整理"算法;
  • 多线程收集;

Parallel Scavenge/Parallel Old收集器运行示意图如下:
在这里插入图片描述
应用场景

  • JDK1.6及之后用来代替老年代的Serial Old收集器;
  • 特别是在Server模式,多CPU的情况下;
  • 在注重吞吐量以及CPU资源敏感场景,就有Parallel Scavenge加Parallel Old收集器"给力"组合

四、CMS收集器

并发标记清理(Concurrent Mark Sweep)收集器也称为并发低停顿收集器(Concurrent Low Pause Collector)或低延迟(low latency)垃圾收集器
在这里插入图片描述

4.1 执行过程

1.初始标记
2.并发标记
3.重新标记
4.并发清除
5.并发重置
上述步骤只有初始化标记和重新标记会STW(Stop The World),其余三个步骤与用户线程都是并发的,下面来看每个步骤具体的细节。

4.1.1 初始标记

标记GC ROOTS直接可达的对象并将其压入标记栈(mark-stack)。标记完之后恢复用户线程。

4.1.2 并发标记

通过初始标记的对象,标记全部最终可达对象。这个过程GC线程与用户线程同时执行,用户线程同时会创建对象,变更对象,导致对象可达性发生改变,这种条件下可能会出现活动对象的漏标的情况,单纯的并发标记操作并不能保证GC的正确性。

所以还需要额外的操作,这个操作就是write barrier。即当赋值引用时,如果赋值的对象还没有被标记,将标记该对象。

4.1.3 重新标记

在并发标记其间,用户线程不断变更对象引用,此时的GC ROOTS有可能会发生变化,于是重新从当前的GC ROOTS和指针更新的区域出发(mod-union table)再进行一次标记,所以这个过程被叫作重新标记。

重新标记只会遍历那些新增没有标记过的活动对象和其间有指针更新的活动对象,如果指针更新频繁,重新标记很有可能会遍历新生代中的大部分甚至全部对象。所以如果重新标记阶段很慢,可以启动一次YGC,来减少并发标记的工作量减少其停顿时间。

4.1.4 并发清除

重新标记结束后,应用程序继续运行,此时分出一个处理器去进行垃圾回收工作。遇到没有被标记的对象(垃圾)就清空掉相应的内存块,并加入可分配列表。遇到被标记的对象保持原来的位置不动,只是重置其标记位,用于下一次GC。(不进行压缩操作,会产生内存碎片)

4.1.5 并发重置

重新调整堆的大小,并为下一次GC做好数据结构支持。

4.2 应对内存碎片化

采用了标记-清除就永远跳不过碎片化这个坑,目前CMS的解决办法是在Full GC时进行内存压缩。FullGC会回退使用SerialGC的标记整理算法进行,这时候GC带来的暂停可能会比较长,这种情况又被称为并发模式失败(Concurrent Mode Failure)

有两个参数配置:UseCMSCompactAtFullCollection 每一次Full GC都进行压缩,CMSFullGCsBeforeCompaction 经过多少次不压缩的Full GC后,执行一次带压缩的Full GC。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值