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/article...

在我的集体网站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 个性能加强%20and%20fixVersion%20%3D%2016%20and%20type%20%3D%20Enhancement)以及25 个 bug 修复%20and%20fixVersion%20%3D%2016%20and%20type%20%3D%20Bug)。曾经能够做到均匀暂停工夫约为 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,也没有说要就义吞吐量(但理论吞吐量有没有升高就不晓得了)。在官网的性能测试图来看,进展工夫也是极低的。

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,一个保持技术原创的博主,我的微信公众号是:编了个程

都看到这儿了,如果感觉我的文章写得还行,无妨反对一下。

文章会首发到公众号,浏览体验最佳,欢送大家关注。

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

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