GC总览
Java是一门面向对象的语言。在使用Java的过程中,会创建大量的对象在内存中。而这些对象,需要在用完之后“回收”掉,释放内存资源。这件事我们称它为垃圾收集( ,简称GC),而实际执行者就是各种各样的“垃圾收集器”( ,以下也简称GC)。
为什么会有各种各样的GC?因为时代在发展,以前的GC可能不能满足现在的需求,所以就会有源源不断的GC推出来。先来看一下都有哪些主流的GC:
新生代:
:单线程,新生代;
: 多线程,新生代;
:多线程,新生代,关注吞吐量;
老年代:
Old: 单线程,的老年代版本;
Old:多线程, 的老年代版本,关注吞吐量;
CMS:多线程,标记-清除算法,关注停顿时间,可以与和配合。
其它
G1:同时负责新生代和老年代,是目前一段时间主流的垃圾收集器(JDK 9到现在JDK 16的默认垃圾收集器)。
ZGC:在大堆下也可以控制STW时间极短(几毫秒内)java递推算法,在JDK 11 为实验阶段,在JDK 15转正。发起,2017年贡献给。
GC:停顿时间极短,在JDK 12为试验阶段,在JDK 15转正。Red Hat发起,与ZGC和G1是竞争关系。
STW: Stop The World,指的是停止用户线程。GC应该尽量避免STW或者缩减STW的时间。
各JDK版本默认GC
下面来看一下从JDK 7开始在默认GC(JDK 7之前的,我就不考古了,现在大家项目上用的也少)。
严格来说之前的版本应该是JDK 1.7, JDK 1.8,这里及下文为了表述方便,我就写成了JDK 7, JDK 8。
聊聊几种主流的GC
考虑到现在大家用的Java版本主要都是JDK 8起,所以主要聊JDK 8及之后可能会用到的GC。
+ Old
以下简称为PS,PO
PS + PO是JDK 7u4之后一直到JDK 9之前模式都在用的默认GC组合。
PS用于新生代,是一个并行的多线程收集器,使用了复制算法。它关注吞吐量,可以通过参数设置最大GC停顿时间、吞吐量的大小等。
PS收集器相比于来说,多了一个“自适应调节策略”。当+y这个参数打开之后,就不需要手动指定新生代的大小,Eden和区的比例java递推算法 盘点Java中的那些常用的Garbage Collector,晋升老年代对象等细节参数了,虚拟机会根据当前系统的运行情况收集性能监控信息,动态调整这些参数以提供最合适的停顿时间或者最大吞吐量。
PO用于老年代,同样是关注吞吐量。它是一个多线程的收集器,采用“标记-整理”算法。PO是在JDK 1.6之后才提供的,目的就是为了与PS配合。在此之前,PS只能与 Old配合(因为框架不适配的原因,不能与CMS配合),老年代GC效率低下,所以PO应运而生。
+ CMS
是一个新生代多线程收集器,使用复制算法。它的出现比PS更早,主要是为了替代单线程低下的效率。在新生代复制期间,使用多线程来降低STW的时间。
CMS是JDK 5才推出的一款关注停顿时间的GC。与PO不同的是,CMS使用的是“标记-清除”算法,这能够让它实现更小的停顿时间,但代价是会产生大量的空间碎片,可能会在大对象晋升老年代时由于没有连续的内存空间,触发full gc。
了解到这里,其实我有两个疑问。
1 CMS为什么不使用“标记-整理”算法?
参考《并发垃圾收集器(CMS)为什么没有采用标记-整理算法来实现?》这篇文章(原文是在iteye的一个讨论,但现在已经找不到了)的说法。GC代码和用户代码需要保持同步,才能保证两者观察到的对象图是一致的。而保持同步有两种做法,一种是read ,一种是write 。因为读远大于写,所以CMS使用的是write 。这就导致CMS如果用“标记-整理”算法的话,需要在“整理”的时候STW,而如果使用“标记-清除”算法的话,在“清除”阶段是不用STW的。
2 为什么CMS从来没作为默认垃圾收集器?
参考知乎这篇文章《为什么 JDK 8 默认使用 收集器?》的回答。总结有三个原因:
然而事实上,有很多互联网大厂选择了CMS+的组合。前段时间美团出了一篇文章《Java中9种常见的CMS GC问题分析与解决》,阿里也用得比较多,所以还是可以了解一下。
G1
前面提到了,G1是一个优秀的后浪。从JDK 9到当前JDK 16一直都是默认的GC。相较于之前的几款GC来说,G1可以说是有一些颠覆性的设计,比如、Card Table、 Set等。
关于G1,需要一篇单独的文章来介绍。我之前在个人网站上有一篇文章介绍,《JVM - G1》
文章地址://82
在我的个人网站,文章页面搜索“G1”即可搜到。
这里大致介绍一下它的特点:
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的探索与实践》(只找到知乎官方号的链接)。
GC
按理说G1和ZGC已经很牛逼了java递推算法,为什么还有其它的GC出来?学不动了啊,有木有。。。
GC,我们称它为SGC吧,它是Red Hat发起的一款GC,与ZGC是竞争关系。更像是G1的继承者,有很多相同之处。它跟ZGC一样,目标也是低停顿时间,不过实现原理有些不同,ZGC是基于 来实现,而 GC是基于 来实现。
SGC没有承诺停顿时间小于10ms,也没有说要牺牲吞吐量(但实际吞吐量有没有降低就不知道了)。在官方的性能测试图来看,停顿时间也是极低的。
GC(3) Pause Init Mark 0.(3) ->() 633.(3) Pause Final Mark 1.(3) ->() 3.(3) ->() 405.(3) Pause Init Refs 0.(3) ->() 354.(3) Pause Final Refs 0.(3) ->() 12.242ms
它只有在标记和更新引用的时候会暂停,看起来也是只有几毫秒的停顿时间。
总结一下
现在市面上很大一部分Java程序都还是基于Java 8(甚至有些是Java 7的),所以PS + PO,和 + CMS都是可以了解一下的,毕竟工作中很有可能会用到。尤其是CMS,大厂用得很多,面试基本上必问。
ZGC和SGC,虽然在JDK 15都已经成为正式版,但实际生产中使用很少,有些潜在问题不一定能很好地发现,可以慢慢去了解和尝试。
可以预见的是,G1会在未来很长一段时间内成为最主流的垃圾收集器,所以很有必要好好了解一下G1,做好知识储备。
求个支持
我是Yasin,一个坚持技术原创的博主,我的微信公众号是:编了个程
都看到这儿了,如果觉得我的文章写得还行,不妨支持一下。
文章会首发到公众号,阅读体验最佳,欢迎大家关注。
你的每一个转发、关注、点赞、评论都是对我最大的支持!
还有学习资源、和一线互联网公司内推哦
求个支持
我是Yasin,一个坚持技术原创的博主,我的微信公众号是:编了个程
都看到这儿了,如果觉得我的文章写得还行,不妨支持一下。
文章会首发到公众号,阅读体验最佳,欢迎大家关注。
你的每一个转发、关注、点赞、评论都是对我最大的支持!
还有学习资源、和一线互联网公司内推哦