盘点Java中的那些常用的Garbage Collector

GC总览

Java是一门面向对象的语言。在使用Java的过程中,会创建大量的对象在内存中。而这些对象,需要在用完之后“回收”掉,释放内存资源。这件事我们称它为垃圾收集(Garbage Collection,简称GC),而实际执行者就是各种各样的“垃圾收集器”(Garbage Collector,以下也简称GC)。

为什么会有各种各样的GC?因为时代在发展,以前的GC可能不能满足现在的需求,所以就会有源源不断的GC推出来。先来看一下都有哪些主流的GC:

新生代:

Serial:单线程,新生代;

ParNew: 多线程,新生代;

Parallel Scavenge:多线程,新生代,关注吞吐量;

老年代:

Serial Old: 单线程,Serial的老年代版本;

Parallel Old:多线程,Parallel Scavenge的老年代版本,关注吞吐量;

CMS:多线程,标记-清除算法,关注停顿时间,可以与Serial和ParNew配合。

其它

G1:同时负责新生代和老年代,是目前一段时间主流的垃圾收集器(JDK 9到现在JDK 16的默认垃圾收集器)。

ZGC:在大堆下也可以控制STW时间极短(几毫秒内),在JDK 11 为实验阶段,在JDK 15转正。Oracle发起,2017年贡献给OpenJDK。

Shenandoah GC:停顿时间极短,在JDK 12为试验阶段,在JDK 15转正。Red Hat发起,与ZGC和G1是竞争关系。

STW: Stop The World,指的是停止用户线程。GC应该尽量避免STW或者缩减STW的时间。

各JDK版本默认GC

下面来看一下从JDK 7开始在默认GC(JDK 7之前的,我就不考古了,现在大家项目上用的也少)。

  • 在JDK 7,默认是Parallel Scavenge + Serial Old。
  • 在JDK 8 及JDK 7u4之后的版本,默认是Parallel Scavenge + Parallel Old。
  • 在JDK 9 到JDK 16,默认是G1

严格来说之前的版本应该是JDK 1.7, JDK 1.8,这里及下文为了表述方便,我就写成了JDK 7, JDK 8。

聊聊几种主流的GC

考虑到现在大家用的Java版本主要都是JDK 8起,所以主要聊JDK 8及之后可能会用到的GC。

Parallel Scavenge + Parallel Old

以下简称为PS,PO

PS + PO是JDK 7u4之后一直到JDK 9之前server模式都在用的默认GC组合。

PS用于新生代,是一个并行的多线程收集器,使用了复制算法。它关注吞吐量,可以通过参数设置最大GC停顿时间、吞吐量的大小等。

PS收集器相比于ParNew来说,多了一个“自适应调节策略”。当+UseAdaptiveSizePolicy这个参数打开之后,就不需要手动指定新生代的大小,Eden和Survivor区的比例,晋升老年代对象等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大吞吐量。

PO用于老年代,同样是关注吞吐量。它是一个多线程的收集器,采用“标记-整理”算法。PO是在JDK 1.6之后才提供的,目的就是为了与PS配合。在此之前,PS只能与Serial Old配合(因为框架不适配的原因,不能与CMS配合),老年代GC效率低下,所以PO应运而生。

ParNew + CMS

ParNew是一个新生代多线程收集器,使用复制算法。它的出现比PS更早,主要是为了替代Serial单线程低下的效率。在新生代复制期间,使用多线程来降低STW的时间。

CMS是JDK 5才推出的一款关注停顿时间的GC。与PO不同的是,CMS使用的是“标记-清除”算法,这能够让它实现更小的停顿时间,但代价是会产生大量的空间碎片,可能会在大对象晋升老年代时由于没有连续的内存空间,触发full gc。

了解到这里,其实我有两个疑问。

1 CMS为什么不使用“标记-整理”算法?

参考《并发垃圾收集器(CMS)为什么没有采用标记-整理算法来实现?》这篇文章(原文是在iteye的一个讨论,但现在已经找不到了)的说法。GC代码和用户代码需要保持同步,才能保证两者观察到的对象图是一致的。而保持同步有两种做法,一种是read barrier,一种是write barrier。因为读远大于写,所以CMS使用的是write barrier。这就导致CMS如果用“标记-整理”算法的话,需要在“整理”的时候STW,而如果使用“标记-清除”算法的话,在“清除”阶段是不用STW的。

2 为什么CMS从来没作为默认垃圾收集器?

参考知乎这篇文章《为什么 JDK 8 默认使用 Parallel Scavenge 收集器?》的回答。总结有三个原因:

  • Java使用服务端的场景为主,服务端更专注吞吐量,所以JDK 8默认的是PS + PO;
  • 使用、调优很复杂,有高达70多个参数
  • “后浪”太优秀,CMS比G1早不了多少,5开始加入,6成熟,9被标记弃用,14被删除。而G1是7加入,8成熟,9正式成为默认。

然而事实上,有很多互联网大厂选择了CMS+ParNew的组合。前段时间美团出了一篇文章《Java中9种常见的CMS GC问题分析与解决》,阿里也用得比较多,所以还是可以了解一下。

G1

前面提到了,G1是一个优秀的后浪。从JDK 9到当前JDK 16一直都是默认的GC。相较于之前的几款GC来说,G1可以说是有一些颠覆性的设计,比如Region、Card Table、Remember Set等。

关于G1,需要一篇单独的文章来介绍。我之前在个人网站上有一篇文章介绍,《JVM - G1》

文章地址:https://yasinshaw.com/articles/82

在我的个人网站https://yasinshaw.com,文章页面搜索“G1”即可搜到。

这里大致介绍一下它的特点:

  • 低延迟优先,即主要侧重于响应能力;
  • 与CMS相同的地方在于,它们都属于并发收集器,在大部分的收集阶段都不需要挂起应用程序;
  • 更精确的预测GC停顿时间,可以根据-XX:MaxGCPauseMillis参数指定停顿时间;
  • 收缩空闲空间不会造成由长GC引起的应用停顿时间;
  • G1没有CMS的碎片化问题(或者说不那么严重)。

ZGC

关于ZGC,笔者了解得也不多。ZGC在JDK 11推出实验版,在JDK 15成为正式版。ZGC的目标是在尽量不牺牲吞吐量(官方宣称,目标是对程序吞吐量影响小于15%)的情况下,做到极低的停顿时间,并且停顿时间不会随着对的内存大小变大而升高。

虽然ZGC在JDK 15已经转正,但还是在不断完善和迭代。JDK 16最近发布,在这个版本中ZGC有46 个功能增强以及25 个 bug 修复。已经可以做到平均暂停时间约为 50 微秒(0.05 毫秒),最大暂停时间约为 500 微秒(0.5 毫秒)。暂停时间不受堆、活动集和根集大小的影响。这个停顿时间,对业务的影响可以说已经微乎其微了,我等只能大呼NB。

想要深入了解ZGC原理的同学,可以参考美团技术的这篇文章《新一代垃圾回收器ZGC的探索与实践》(只找到知乎官方号的链接)。

Shenandoah GC

按理说G1和ZGC已经很牛逼了,为什么还有其它的GC出来?学不动了啊,有木有。。。

Shenandoah GC,我们称它为SGC吧,它是Red Hat发起的一款GC,与ZGC是竞争关系。Shenandoah更像是G1的继承者,有很多相同之处。它跟ZGC一样,目标也是低停顿时间,不过实现原理有些不同,ZGC是基于colored pointers来实现,而Shenandoah GC是基于brooks pointers来实现。

SGC没有承诺停顿时间小于10ms,也没有说要牺牲吞吐量(但实际吞吐量有没有降低就不知道了)。在官方的性能测试图来看,停顿时间也是极低的。

SGC性能报告

GC(3) Pause Init Mark 0.771ms GC(3) Concurrent marking 76480M->77212M(102400M) 633.213ms GC(3) Pause Final Mark 1.821ms GC(3) Concurrent cleanup 77224M->66592M(102400M) 3.112ms GC(3) Concurrent evacuation 66592M->75640M(102400M) 405.312ms GC(3) Pause Init Update Refs 0.084ms GC(3) Concurrent update references 75700M->76424M(102400M) 354.341ms GC(3) Pause Final Update Refs 0.409ms GC(3) Concurrent cleanup 76244M->56620M(102400M) 12.242ms

它只有在标记和更新引用的时候会暂停,看起来也是只有几毫秒的停顿时间。

总结一下

现在市面上很大一部分Java程序都还是基于Java 8(甚至有些是Java 7的),所以PS + PO,和ParNew + CMS都是可以了解一下的,毕竟工作中很有可能会用到。尤其是CMS,大厂用得很多,面试基本上必问。

ZGC和SGC,虽然在JDK 15都已经成为正式版,但实际生产中使用很少,有些潜在问题不一定能很好地发现,可以慢慢去了解和尝试。

可以预见的是,G1会在未来很长一段时间内成为最主流的垃圾收集器,所以很有必要好好了解一下G1,做好知识储备。

求个支持

我是Yasin,一个坚持技术原创的博主,我的微信公众号是:编了个程

都看到这儿了,如果觉得我的文章写得还行,不妨支持一下。

文章会首发到公众号,阅读体验最佳,欢迎大家关注。

你的每一个转发、关注、点赞、评论都是对我最大的支持!

还有学习资源、和一线互联网公司内推哦

求个支持

我是Yasin,一个坚持技术原创的博主,我的微信公众号是:编了个程

都看到这儿了,如果觉得我的文章写得还行,不妨支持一下。

文章会首发到公众号,阅读体验最佳,欢迎大家关注。

你的每一个转发、关注、点赞、评论都是对我最大的支持!

还有学习资源、和一线互联网公司内推哦

热门文章

暂无图片
编程学习 ·

C语言二分查找详解

二分查找是一种知名度很高的查找算法,在对有序数列进行查找时效率远高于传统的顺序查找。 下面这张动图对比了二者的效率差距。 二分查找的基本思想就是通过把目标数和当前数列的中间数进行比较,从而确定目标数是在中间数的左边还是右边,将查…
暂无图片
编程学习 ·

GMX 命令分类列表

建模和计算操作命令: 1.1 . 创建拓扑与坐标文件 gmx editconf - 编辑模拟盒子以及写入子组(subgroups) gmx protonate - 结构质子化 gmx x2top - 根据坐标生成原始拓扑文件 gmx solvate - 体系溶剂化 gmx insert-molecules - 将分子插入已有空位 gmx genconf - 增加…
暂无图片
编程学习 ·

一文高效回顾研究生课程《数值分析》重点

数值分析这门课的本质就是用离散的已知点去估计整体,就是由黑盒子产生的结果去估计这个黑盒子。在数学里这个黑盒子就是一个函数嘛,这门课会介绍许多方法去利用离散点最大化地逼近这个函数,甚至它的导数、积分,甚至微分方程的解。…
暂无图片
编程学习 ·

在职阿里5年,一个28岁女软测工程师的心声

简单的先说一下,坐标杭州,14届本科毕业,算上年前在阿里巴巴的面试,一共有面试了有6家公司(因为不想请假,因此只是每个晚上去其他公司面试,所以面试的公司比较少) ​ 编辑切换为居中…
暂无图片
编程学习 ·

字符串左旋c语言

目录 题目: 解题思路: 第一步: 第二步: 第三步: 总代码: 题目: 实现一个函数,可以左旋字符串中的k个字符。 例如: ABCD左旋一个字符得到BCDA ABCD左旋两个字符…
暂无图片
编程学习 ·

设计模式--观察者模式笔记

模式的定义与特点 观察者(Observer)模式的定义:指多个对象间存在一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。这种模式有时又称作发布-订阅模式、模型-视图模式&#xf…
暂无图片
编程学习 ·

睡觉突然身体动不了,什么是睡眠痽痪症

很多朋友可能有这样的体验,睡觉过程中突然意识清醒,身体却动弹不了。这时候感觉非常恐怖,希望旁边有一个人推自己一下。阳光以前也经常会碰到这样的情况,一年有一百多次,那时候很害怕晚上到来,睡觉了就会出…
暂无图片
编程学习 ·

深入理解C++智能指针——浅析MSVC源码

文章目录unique_ptrshared_ptr 与 weak_ptrstd::bad_weak_ptr 异常std::enable_shared_from_thisunique_ptr unique_ptr 是一个只移型别(move-only type,只移型别还有std::mutex等)。 结合一下工厂模式,看看其基本用法&#xff…
暂无图片
编程学习 ·

@TableField(exist = false)

TableField(exist false) //申明此字段不在数据库存在,但代码中需要用到它,通知Mybatis-plus在做写库操作是忽略它。,.
暂无图片
编程学习 ·

Java Web day15

第十二章文件上传和下载 一、如何实现文件上传 要实现Web开发中的文件上传功能,通常需要完成两步操作:一.是在Web页面中添加上传输入项;二是在Servlet中读取上传文件的数据,并保存到本地硬盘中。 需要使用一个Apache组织提供一个…
暂无图片
编程学习 ·

【51nod 2478】【单调栈】【前缀和】小b接水

小b接水题目解题思路Code51nod 2478 小b接水 题目 输入样例 12 0 1 0 2 1 0 1 3 2 1 2 1输出样例 6解题思路 可以发现最后能拦住水的都是向两边递减高度(?) 不管两个高积木之间的的积木是怎样乱七八糟的高度,最后能用来装水的…
暂无图片
编程学习 ·

花了大半天写了一个UVC扩展单元调试工具

基于DIRECTSHOW 实现的,用的是MFC VS2019. 详见:http://www.usbzh.com/article/detail-761.html 获取方法 加QQ群:952873936,然后在群文件\USB调试工具&测试软件\UVCXU-V1.0(UVC扩展单元调试工具-USB中文网官方版).exe USB中文网 USB中文…
暂无图片
编程学习 ·

贪心(一):区间问题、Huffman树

区间问题 例题一:区间选点 给定 N 个闭区间 [ai,bi]请你在数轴上选择尽量少的点,使得每个区间内至少包含一个选出的点。 输出选择的点的最小数量。 位于区间端点上的点也算作区间内。 输入格式 第一行包含整数 N,表示区间数。 接下来 …
暂无图片
编程学习 ·

C语言练习实例——费氏数列

目录 题目 解法 输出结果 题目 Fibonacci为1200年代的欧洲数学家,在他的着作中曾经提到:「若有一只免子每个月生一只小免子,一个月后小免子也开始生产。起初只有一只免子,一个月后就有两只免子,二个月后有三只免子…
暂无图片
编程学习 ·

Android开发(2): Android 资源

个人笔记整理 Android 资源 Android中的资源,一般分为两类: 系统内置资源:Android SDK中所提供的已经定义好的资源,用户可以直接拿来使用。 用户自定义资源:用户自己定义或引入的,只适用于当前应用的资源…
暂无图片
编程学习 ·

零基础如何在短时间内拿到算法offer

​算法工程师是利用算法处理事物的职业 算法(Algorithm)是一系列解决问题的清晰指令,也就是说,能够对一定规范的输入,在有限时间内获得所要求的输出。 如果一个算法有缺陷,或不适合于某个问题,执…
暂无图片
编程学习 ·

人工智能:知识图谱实战总结

人工智能python,NLP,知识图谱,机器学习,深度学习人工智能:知识图谱实战前言一、实体建模工具Protegepython,NLP,知识图谱,机器学习,深度学习 人工智能:知识图…
暂无图片
编程学习 ·

【无标题】

这里写自定义目录标题欢迎使用Markdown编辑器新的改变功能快捷键合理的创建标题,有助于目录的生成如何改变文本的样式插入链接与图片如何插入一段漂亮的代码片生成一个适合你的列表创建一个表格设定内容居中、居左、居右SmartyPants创建一个自定义列表如何创建一个注…