关于java:JVM学习垃圾回收器

31次阅读

共计 20045 个字符,预计需要花费 51 分钟才能阅读完成。

一、GC 分类与性能指标

1.1 垃圾收集器分类

(1)按线程数分

按线程数分(垃圾回收线程数),能够分为串行垃圾回收器和并行垃圾回收器。

串行回收指的是在同一时间段内只容许有一个 CPU 用于执行垃圾回收操作,此时工作线程被暂停,直至垃圾收集工作完结。

  • 在诸如单 CPU 处理器或者较小的利用内存等硬件平台不是特地优越的场合,串行回收器的性能体现能够超过并行回收器和并发回收器。所以,串行回收默认被利用在客户端的 Client 模式下的 JVM 中
  • 在并发能力比拟强的 CPU 上,并行回收器产生的进展工夫要短于串行回收器。

和串行回收相同,并行收集能够使用多个 CPU 同时执行垃圾回收,因而晋升了利用的吞吐量,不过并行回收依然与串行回收一样,采纳独占式,应用了“stop-the-world”机制。

(2)按工作模式分

依照工作模式分,能够分为并发式垃圾回收器和独占式垃圾回收器。

  • 并发式垃圾回收器与应用程序线程交替工作,以尽可能减少应用程序的进展工夫。
  • 独占式垃圾回收器(Stop the world)一旦运行,就进行应用程序中的所有用户线程,直到垃圾回收过程齐全完结。

(3)其余分类

按碎片解决形式分,可分为压缩武垃圾回收器和非压缩式垃圾回收器。

  • 压缩式垃圾回收器会在回收实现后,对存活对象进行压缩整顿,打消回收后的碎片。

    • 再调配对象空间应用:指针碰撞
  • 非压缩式的垃圾回收器不进行这步操作。

    • 再调配对象空间应用:闲暇列表

还能够按工作的内存区间分,又分为年老代垃圾回收器和老年代垃圾回收器。

1.2 性能指标

  • 吞吐量:运行用户代码的工夫占总运行工夫的比例

    • (总运行工夫 = 程序的运行工夫 + 内存回收的工夫)
  • 垃圾收集开销:吞吐量的补数,垃圾收集所用工夫与总运行工夫的比例。
  • 暂停工夫:执行垃圾收集时,程序的工作线程被暂停的工夫。
  • 收集频率:绝对于应用程序的执行,收集操作产生的频率。
  • 内存占用:Java 堆区所占的内存大小。
  • 疾速:一个对象从诞生到被回收所经验的工夫。

吞吐量、暂停工夫、内存占用 这三者独特形成一个“不可能三角”。三者总体的体现会随着技术提高而越来越好。

这三项里,暂停工夫的重要性日益凸显。因为随着硬件倒退,内存占用多些越来越能容忍,硬件性能的晋升也有助于升高收集器运行时对应用程序的影响,即进步了吞吐量。而内存的扩充,对提早反而带来负面成果。
简略来说,次要抓住两点:

  • 吞吐量
  • 暂停工夫

(1)性能指标:吞吐量

吞吐量就是 CPU 用于运行用户代码的工夫与 CPU 总耗费工夫的比值,即吞吐量 = 运行用户代码工夫 /(运行用户代码工夫 + 垃圾收集工夫)

比方:虚拟机总共运行了 100 分钟,其中垃圾收集花掉 1 分钟,那吞吐量就是 99%。

这种状况下,应用程序能容忍较高的暂停工夫,因而,高吞吐量的应用程序有更长的工夫基准,疾速响应是不用思考的。

吞吐量优先,意味着在单位工夫内,STW 的工夫最短:0.2+0.2=0.4

(2)性能指标:暂停工夫

“暂停工夫”是指一个时间段内应用程序线程暂停,让 GC 线程执行的状态。

例如,GC 期间 1ee 毫秒的暂停工夫意味着在这 100 毫秒期间内没有应用程序线程是流动的。暂停工夫优先,意味着尽可能让单次 STW 的工夫最短:0.1+0.1 + 0.1+ 0.1+ 0.1=0.5s

(3)吞吐量 vs 暂停工夫

高吞吐量较好因为这会让应用程序的最终用户感觉只有应用程序线程在做“生产性”工作。直觉上,吞吐量越高程序运行越快。

低暂停工夫(低提早)较好因为从最终用户的角度来看不论是 GC 还是其余起因导致一个利用被挂起始终是不好的。这取决于应用程序的类型,有时候甚至短暂的 200 毫秒暂停都可能打断终端用户体验。因而,具备低的较大暂停工夫是十分重要的,特地是对于一个交互式应用程序。

可怜的是”高吞吐量”和”低暂停工夫”是一对相互竞争的指标(矛盾)。

因为如果抉择以吞吐量优先,那么必然须要升高内存回收的执行频率,然而这样会导致 GC 须要更长的暂停工夫来执行内存回收。

相同的,如果抉择以低提早优先为准则,那么为了升高每次执行内存回收时的暂停工夫,也只能频繁地执行内存回收,但这又引起了年老代内存的缩减和导致程序吞吐量的降落。

在设计(或应用)GC 算法时,咱们必须确定咱们的指标:一个 GC 算法只可能针对两个指标之一(即只专一于较大吞吐量或最小暂停工夫),或尝试找到一个二者的折中。

当初规范:在最大吞吐量优先的状况下,升高暂停工夫

二、不同的垃圾回收器概述

垃圾收集机制是 Java 的招牌能力,极大地提高了开发效率。这当然也是面试的热点。

那么,Java 常见的垃圾收集器有哪些?

GC 垃圾收集器是和 JVM 一脉相承的,它是和 JVM 进行搭配应用,在不同的应用场景对应的收集器也是有区别

2.1 垃圾回收器发展史

有了虚拟机,就肯定须要收集垃圾的机制,这就是 Garbage Collection,对应的产品咱们称为 Garbage Collector。

  • 1999 年随 JDK1.3.1 一起来的是串行形式的 serialGc,它是第一款 GC。ParNew 垃圾收集器是 Serial 收集器的多线程版本。
  • 2002 年 2 月 26 日,Parallel GC 和 Concurrent Mark Sweep GC 追随 JDK1.4.2 一起公布。
  • Parallel GC 在 JDK6 之后成为 HotSpot 默认 GC。
  • 2012 年,在 JDK1.7u4 版本中,G1 可用。
  • 2017 年,JDK9 中 G1 变成默认的垃圾收集器,以代替 CMS。
  • 2018 年 3 月,JDK10 中 G1 垃圾回收器的并行残缺垃圾回收,实现并行性来改善最坏状况下的提早。
  • 2018 年 9 月,JDK11 公布。引入 Epsilon 垃圾回收器,又被称为 “No-Op(无操作)“回收器。同时,引入 ZGC:可伸缩的低提早垃圾回收器(Experimental)。
  • 2019 年 3 月,JDK12 公布。加强 G1,主动返回未用堆内存给操作系统。同时,引入 Shenandoah GC:低进展工夫的 GC(Experimental)。
  • 2019 年 9 月,JDK13 公布。加强 YGC,主动返回未用堆内存给操作系统。
  • 2020 年 3 月,JDK14 公布。删除 CMS 垃圾回收器。扩大 ZGC 在 macos 和 Windows 上的利用。

2.2 7 种经典的垃圾收集器

  • 串行回收器:Serial、Serial old
  • 并行回收器:ParNew、Parallel Scavenge、Parallel old
  • 并发回收器:CMS、G11

7 款经典收集器与垃圾分代之间的关系

新生代收集器:Serial、ParNew、Parallel Scavenge

老年代收集器:Serial old、Parallel old、CMS

整堆收集器:G1

2.3 垃圾收集器的组合关系

  • 两个收集器间有连线,表明它们能够搭配应用:Serial/Serial Old、Serial/CMS、ParNew/Serial Old、ParNew/CMS、Parallel Scavenge/Serial Old、Parallel Scavenge/Parallel Old、G1;
  • 其中 Serial Old 作为 CMS 呈现 ”Concurrent Mode Failure” 失败的后备预案。
  • (红色虚线)因为保护和兼容性测试的老本,在 JDK 8 时将 Serial+CMS、ParNew+Serial Old 这两个组合申明为废除(JEP173),并在 JDK9 中齐全勾销了这些组合的反对(JEP214),即:移除。
  • (绿色虚线)JDK14 中:弃用 Parallel Scavenge 和 Serialold GC 组合(JEP366)
  • (青色虚线)JDK14 中:删除 CMS 垃圾回收器(JEP363)

为什么要有很多收集器,一个不够吗?因为 Java 的应用场景很多,挪动端,服务器等。所以就须要针对不同的场景,提供不同的垃圾收集器,进步垃圾收集的性能。

尽管咱们会对各个收集器进行比拟,但并非为了筛选一个最好的收集器进去。没有一种放之四海皆准、任何场景下都实用的完满收集器存在,更加没有万能的收集器。所以咱们 抉择的是对具体利用最合适的收集器

如何查看默认垃圾收集器

-XX:+PrintCommandLineFlags:查看命令行相干参数(蕴含应用的垃圾收集器)

应用命令行指令:jinfo -flag 相干垃圾回收器参数 过程 ID

三、Serial 回收器:串行回收

Serial 收集器是最根本、历史最悠久的垃圾收集器了。JDK1.3 之前回收新生代惟一的抉择。

Serial 收集器作为 HotSpot 中 Client 模式下的默认新生代垃圾收集器,采纳复制算法、串行回收和 ”Stop-The-World” 机制的形式执行内存回收

除了年老代之外,Serial 收集器还提供用于执行老年代垃圾收集的 Serial Old 收集器。Serial Old 收集器同样也采纳了串行回收和 ”Stop-The-World” 机制,只不过内存回收算法应用的是标记 - 压缩算法

  • Serial Old 是运行在 Client 模式下默认的老年代的垃圾回收器
  • Serial Old 在 Server 模式下次要有两个用处:

    • 与新生代的 Parallel scavenge 配合应用
    • 作为老年代 CMS 收集器的后备垃圾收集计划

这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅阐明它 只会应用一个 CPU 或一条收集线程去实现垃圾收集工作 ,更重要的是在它进行垃圾收集时, 必须暂停其余所有的工作线程,直到它收集完结(Stop The World)

劣势:简略而高效(与其余收集器的单线程比),对于限定单个 CPU 的环境来说,Serial 收集器因为没有线程交互的开销,分心做垃圾收集天然能够取得最高的单线程收集效率。运行在 Client 模式下的虚拟机是个不错的抉择。

在用户的桌面利用场景中,可用内存个别不大(几十 MB 至一两百 MB),能够在较短时间内实现垃圾收集(几十 ms 至一百多 ms),只有不频繁产生,应用串行回收器是能够承受的。

在 HotSpot 虚拟机中,应用 -XX:+UseSerialGC 参数能够指定年老代和老年代都应用串行收集器。

四、ParNew 回收器:并行回收

如果说 SerialGC 是年老代中的单线程垃圾收集器,那么 ParNew 收集器则是 Serial 收集器的多线程版本。

  • Par 是 Parallel 的缩写,New:只能解决的是新生代

ParNew 收集器除了采纳 并行回收 的形式执行内存回收外,两款垃圾收集器之间简直没有任何区别。ParNew 收集器在年老代中同样也是 采纳复制算法、”Stop-The-World” 机制

ParNew 是很多 JVM 运行在 Server 模式下新生代的默认垃圾收集器。

  • 对于新生代,回收次数频繁,应用并行形式高效。
  • 对于老年代,回收次数少,应用串行形式节俭资源。(CPU 并行须要切换线程,串行能够省去切换线程的资源)
  • 除 Serial 外,目前只有 ParNew GC 能与 CMS 收集器配合工作。

在程序中,开发人员能够通过选项 -XX:+UseParNewGC 手动指定应用 ParNew 收集器执行内存回收工作。它示意年老代应用并行收集器,不影响老年代。

-XX:ParallelGCThreads限度线程数量,默认开启和 CPU 数据雷同的线程数。

五、Parallel 回收器:吞吐量优先

HotSpot 的年老代中除了领有 ParNew 收集器是基于并行回收的以外,Parallel Scavenge 收集器同样也采纳了 复制算法、并行回收和 ”Stop-The-World” 机制

那么 Parallel 收集器的呈现是否多此一举?

  • 和 ParNew 收集器不同,ParallelScavenge 收集器的指标则是 达到一个可管制的吞吐量(Throughput),它也被称为吞吐量优先的垃圾收集器。
  • 自适应调节策略也是 Parallel Scavenge 与 ParNew 一个重要区别。

高吞吐量则能够高效率地利用 CPU 工夫,尽快实现程序的运算工作,次要适宜在 后盾运算而不须要太多交互的工作 。因而,常见在服务器环境中应用。例如, 那些执行批量解决、订单解决、工资领取、科学计算的应用程序

Parallel 收集器在 JDK1.6 时提供了用于执行老年代垃圾收集的 Parallel Old 收集器,用来代替老年代的 Serial Old 收集器。

Parallel Old 收集器采纳了标记 - 压缩算法,但同样也是基于并行回收和 ”Stop-The-World” 机制。

在程序吞吐量优先的利用场景中,Parallel 收集器和 Parallel Old 收集器的组合,在 Server 模式下的内存回收性能很不错。在 Java8 中,默认是此垃圾收集器

参数配置

-XX:+UseParallelGC:手动指定年老代应用 Parallel 并行收集器执行内存回收工作。

-XX:+UseParallelOldGC:手动指定老年代都是应用并行回收收集器。

  • 别离实用于新生代和老年代。默认 jdk8 是开启的。
  • 下面两个参数,默认开启一个,另一个也会被开启。(相互激活

-XX:ParallelGCThreads:设置年老代并行收集器的线程数。

  • 个别地,最好与 CPU 数量相等,以防止过多的线程数影响垃圾收集性能。
  • 在默认状况下,当 CPU 数量小于 8 个,ParallelGCThreads 的值等于 CPU 数量;当 CPU 数量大于 8 个,ParallelGCThreads 的值等于 3 +[5*CPU Count]/8]

XX:MaxGCPauseMillis:设置垃圾收集器最大进展工夫(即 STW 的工夫),单位是毫秒。(慎用

  • 为了尽可能地把进展工夫管制在 MaxGCPauseMi11s 以内,收集器在工作时会调整 Java 堆大小或者其余一些参数。对于用户来讲,进展工夫越短体验越好。然而在服务器端,咱们重视高并发,整体的吞吐量。所以服务器端适宜 Parallel,进行管制。

-XX:GCTimeRatio:垃圾收集工夫占总工夫的比例(= 1/(N+1))。用于掂量吞吐量的大小,取值范畴(0,100),默认值 99,也就是垃圾回收工夫不超过 1。

  • 与前一个 -XX:MaxGCPauseMillis 参数有肯定矛盾性。暂停工夫越长,Radio 参数就容易超过设定的比例。

-XX:+UseAdaptivesizepplicy :设置 Parallel scavenge 收集器具备自适应调节策略。

  • 在这种模式下,年老代的大小、Eden 和 Survivor 的比例、降职老年代的对象年龄等参数会被主动调整,已达到在堆大小、吞吐量和进展工夫之间的平衡点。
  • 在手动调优比拟艰难的场合,能够间接应用这种自适应的形式,仅指定虚拟机的最大堆、指标的吞吐量(GCTimeRatio)和进展工夫(MaxGCPauseMil1s),让虚拟机本人实现调优工作。

六、CMS 回收器:低提早

6.1 概述

在 JDK1.5 期间,Hotspot 推出了一款在强交互利用中简直可认为有划时代意义的垃圾收集器:cMS(Concurrent-Mark-Sweep)收集器,这款收集器是 HotSpot 虚拟机中第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程同时工作

CMS 收集器的关注点是尽可能缩短垃圾收集时用户线程的进展工夫。进展工夫越短(低提早)就越适宜与用户交互的程序,良好的响应速度能晋升用户体验。

目前 很大一部分的 Java 利用集中在互联网站或者 B / S 零碎的服务端上,这类利用尤其器重服务的响应速度,心愿零碎进展工夫最短,以给用户带来较好的体验。CMS 收集器就十分合乎这类利用的需要。

CMS 的垃圾收集算法采纳 标记 - 革除算法,并且也会 ”Stop-The-World”。

可怜的是,CMS 作为老年代的收集器,却无奈与 JDK1.4.0 中曾经存在的新生代收集器 Parallel Scavenge 配合工作,所以在 JDK1.5 中应用 CMS 来收集老年代的时候,新生代只能抉择 ParNew 或者 Serial 收集器中的一个。

在 G1 呈现之前,CMS 应用还是十分宽泛的。始终到明天,依然有很多零碎应用 CMS GC。

CMS 整个过程比之前的收集器要简单,整个过程分为 4 个次要阶段,即初始标记阶段、并发标记阶段、从新标记阶段和并发革除阶段。(波及 STW 的阶段次要是:初始标记 和 从新标记)

  • 初始标记 (Initial-Mark)阶段:在这个阶段中,程序中所有的工作线程都将会因为“stop-the-world”机制而呈现短暂的暂停,这个阶段的次要工作仅仅只是 标记出 GC Roots 能间接关联到的对象。一旦标记实现之后就会复原之前被暂停的所有利用线程。因为间接关联对象比拟小,所以这里的速度十分快。
  • 并发标记(Concurrent-Mark)阶段:从 GC Roots 的间接关联对象开始遍历整个对象图的过程,这个过程耗时较长然而不须要进展用户线程,能够与垃圾收集线程一起并发运行。
  • 从新标记(Remark)阶段:因为在并发标记阶段中,程序的工作线程会和垃圾收集线程同时运行或者穿插运行,因而为了修改并发标记期间,因用户程序持续运作而导致标记产生变动的那一部分对象的标记记录,这个阶段的进展工夫通常会比初始标记阶段稍长一些,但也远比并发标记阶段的工夫短。
  • 并发革除(Concurrent-Sweep)阶段:此阶段清理删除掉标记阶段判断的曾经死亡的对象,开释内存空间。因为不须要挪动存活对象,所以这个阶段也是能够与用户线程同时并发的

6.2 优缺点剖析

只管 CMS 收集器采纳的是并发回收(非独占式),然而在其初始化标记和再次标记这两个阶段中依然须要执行“Stop-The-World”机制暂停程序中的工作线程,不过暂停工夫并不会太长,因而能够阐明目前所有的垃圾收集器都做不到齐全不须要“Stop-The-World”,只是尽可能地缩短暂停工夫。

因为最消耗工夫的并发标记与并发革除阶段都不须要暂停工作,所以整体的回收是低进展的。

另外,因为在垃圾收集阶段用户线程没有中断,所以在 CMS 回收过程中,还应该确保应用程序用户线程有足够的内存可用。因而,CMS 收集器不能像其余收集器那样等到老年代简直齐全被填满了再进行收集,而是当堆内存使用率达到某一阈值时,便开始进行回收,以确保应用程序在 CMS 工作过程中仍然有足够的空间反对利用程序运行。要是 CMS 运行期间预留的内存无奈满足程序须要,就会呈现一次“Concurrent Mode Failure”失败,这时虚拟机将启动后备预案:长期启用 Serial old 收集器来从新进行老年代的垃圾收集,这样进展工夫就很长了。

CMS 收集器的垃圾收集算法采纳的是 标记革除算法,这意味着每次执行完内存回收后,因为被执行内存回收的无用对象所占用的内存空间极有可能是不间断的一些内存块,不可避免地将会产生一些内存碎片。那么 CMS 在为新对象分配内存空间时,将无奈应用指针碰撞(Bump the Pointer)技术,而只可能抉择闲暇列表(Free List)执行内存调配。

CMS 为什么不应用标记整顿算法?

答案其实很简答,因为当并发革除的时候,用 Compact 整顿内存的话,原来的用户线程应用的内存还怎么用呢?要保障用户线程能继续执行,前提的它运行的资源不受影响嘛。Mark Compact 更适宜“stop the world”这种场景下应用。

长处

  • 并发收集
  • 低提早

毛病

  • 会产生内存碎片,导致并发革除后,用户线程可用的空间有余。在无奈调配大对象的状况下,不得不提前触发 FullGC。
  • CMS 收集器对 CPU 资源十分敏感。在并发阶段,它尽管不会导致用户进展,然而会因为占用了一部分线程而导致应用程序变慢,总吞吐量会升高。
  • CMS 收集器无奈解决浮动垃圾。可能呈现“Concurrent Mode Failure” 失败而导致另一次 Full GC 的产生。在并发标记阶段因为程序的工作线程和垃圾收集线程是同时运行或者穿插运行的,那么在并发标记阶段如果产生新的垃圾对象,CMS 将无奈对这些垃圾对象进行标记,最终会导致这些新产生的垃圾对象没有被及时回收,从而只能在下一次执行 GC 时开释这些之前未被回收的内存空间。

6.3 设置参数

  • -XX:+UseConcMarkSweepGC手动指定应用 CMS 收集器执行内存回收工作。

开启该参数后会主动将 -XX:+UseParNewGC 关上。即:ParNew(Young 区用)+CMS(Old 区用)+Serial Old 的组合。

  • -XX:CMSInitiatingOccupanyFraction 设置堆内存使用率的阈值,一旦达到该阈值,便开始进行回收。

JDK5 及以前版本的默认值为 68,即当老年代的空间使用率达到 68% 时,会执行一次 CMS 回收。JDK6 及以上版本默认值为 92%

如果内存增长迟缓,则能够设置一个稍大的值,大的阀值能够无效升高 CMS 的触发频率,缩小老年代回收的次数能够较为显著地改善应用程序性能。反之,如果应用程序内存使用率增长很快,则应该升高这个阈值,以防止频繁触发老年代串行收集器。因而通过该选项便能够 无效升高 Full GC 的执行次数

  • -XX:+UseCMSCompactAtFullCollection用于指定在执行完 Full GC。

GC 后对内存空间进行压缩整顿,以此防止内存碎片的产生。不过因为内存压缩整顿过程无奈并发执行,所带来的问题就是进展工夫变得更长了。

  • -XX:CMSFullGCsBeforecompaction 设置在执行多少次 Full GC 后对内存空间进行压缩整顿。
  • -XX:ParallelcMSThreads 设置 CMS 的线程数量。

CMS 默认启动的线程数是(ParallelGCThreads+3)/4,ParallelGCThreads 是年老代并行收集器的线程数。当 CPU 资源比拟缓和时,受到 CMS 收集器线程的影响,应用程序的性能在垃圾回收阶段可能会十分蹩脚。

小结

HotSpot 有这么多的垃圾回收器,那么如果有人问,Serial GC、Parallel GC、Concurrent Mark Sweep GC 这三个 GC 有什么不同呢?

  • 如果你想要最小化地应用内存和并行开销,请选 Serial GC;
  • 如果你想要最大化应用程序的吞吐量,请选 Parallel GC;
  • 如果你想要最小化 GC 的中断或进展工夫,请选 CMs GC。

JDK 后续版本中 CMS 的变动

JDK9 新个性:CMS 被标记为 Deprecate 了(JEP291)> 如果对 JDK9 及以上版本的 HotSpot 虚拟机应用参数

-XX:+UseConcMarkSweepGC来开启 CMS 收集器的话,用户会收到一个正告信息,提醒 CMS 将来将会被废除。

JDK14 新个性:删除 CMS 垃圾回收器(JEP363)移除了 CMS 垃圾收集器,如果在 JDK14 中应用
XX:+UseConcMarkSweepGC 的话,JVM 不会报错,只是给出一个 warning 信息,然而不会 exit。JVM 会主动回退以默认 GC 形式启动 JVM。

七、G1 回收器:区域化分代式

7.1 概述

既然咱们曾经有了后面几个弱小的 GC,为什么还要公布 Garbage First(G1)?

起因就在于应用程序所应答的业务越来越宏大、简单,用户越来越多,没有 GC 就不能保障应用程序失常进行,而常常造成 STW 的 GC 又跟不上理论的需要,所以才会一直地尝试对 GC 进行优化。G1(Garbage-First)垃圾回收器是在 Java7 update4 之后引入的一个新的垃圾回收器,是当今收集器技术倒退的最前沿成绩之一。

与此同时,为了适应当初不断扩大的内存和一直减少的处理器数量,进一步升高暂停工夫(pause time),同时兼顾良好的吞吐量。

官网给 G1 设定的指标是在提早可控的状况下取得尽可能高的吞吐量,所以才担当起“全功能收集器”的重任与冀望

为什么名字叫 Garbage First(G1)呢?

因为 G1 是一个并行回收器,它把堆内存宰割为很多不相干的区域(Region)(物理上不间断的)。应用不同的 Region 来示意 Eden、幸存者 0 区,幸存者 1 区,老年代等。

G1 GC 有打算地防止在整个 Java 堆中进行全区域的垃圾收集。G1 跟踪各个 Region 外面的垃圾沉积的价值大小(回收所取得的空间大小以及回收所需工夫的经验值),在后盾保护一个优先列表,每次依据容许的收集工夫,优先回收价值最大的 Region

因为这种形式的侧重点在于回收垃圾最大量的区间(Region),所以咱们给 G1 一个名字:垃圾优先(Garbage First)。

G1(Garbage-First)是一款面向服务端利用的垃圾收集器,次要针对装备多核 CPU 及大容量内存的机器,以极高概率满足 GC 进展工夫的同时,还兼具高吞吐量的性能特色。

在 JDK1.7 版本正式启用,移除了 Experimenta1 的标识,是 JDK9 当前的默认垃圾回收器,取代了 CMS 回收器以及 Parallel+Parallel Old 组合。被 orac1e 官网称为“全功能的垃圾收集器”。

与此同时,CMS 曾经在 JDK9 中被标记为废除(Deprecated)。在 jdk8 中还不是默认的垃圾回收器,须要应用 -XX:+UseG1GC 来启用。

7.2 优缺点

(1)G1 垃圾收集器的长处

与其余 GC 收集器相比,G1 应用了全新的分区算法,其特点如下所示:

并行与并发

  • 并行性:G1 在回收期间,能够有多个 GC 线程同时工作,无效利用多核计算能力。此时用户线程 STW。
  • 并发性:G1 领有与应用程序交替执行的能力,局部工作能够和应用程序同时执行。因而,一般来说,不会在整个回收阶段产生齐全阻塞应用程序的状况。

分代收集

  • 从分代上看,G1 仍然属于分代型垃圾回收器,它会辨别年老代和老年代,年老代仍然有 Eden 区和 Survivor 区。但从堆的构造上看,它不要求整个 Eden 区、年老代或者老年代都是间断的,也不再保持固定大小和固定数量。
  • 将堆空间分为若干个区域(Region),这些区域中蕴含了逻辑上的年老代和老年代。
  • 和之前的各类回收器不同,它同时兼顾年老代和老年代。比照其余回收器,或者工作在年老代,或者工作在老年代;

G1 所谓的分代,曾经不是上面这样的:

而是这样的一个区域:

空间整合

  • CMS:“标记 - 革除”算法、内存碎片、若干次 GC 后进行一次碎片整顿。
  • G1 将内存划分为一个个的 region。内存的回收是以 region 作为根本单位的。Region 之间是复制算法 ,但整体上理论可看作是 标记 - 压缩(Mark-Compact)算法,两种算法都能够防止内存碎片。这种个性有利于程序长时间运行,调配大对象时不会因为无奈找到间断内存空间而提前触发下一次 GC。尤其是当 Java 堆十分大的时候,G1 的劣势更加显著。

可预测的进展工夫模型(即:软实时 soft real-time)
这是 G1 绝对于 CMS 的另一大劣势,G1 除了谋求低进展外,还能建设可预测的进展工夫模型,能让使用者明确指定在一个长度为 M 毫秒的工夫片段内,耗费在垃圾收集上的工夫不得超过 N 毫秒。

  • 因为分区的起因,G1 能够只选取局部区域进行内存回收,这样放大了回收的范畴,因而对于全局进展状况的产生也能失去较好的管制。
  • G1 跟踪各个 Region 外面的垃圾沉积的价值大小(回收所取得的空间大小以及回收所需工夫的经验值),在后盾保护一个优先列表,每次依据容许的收集工夫,优先回收价值最大的 Region。保障了 G1 收集器在无限的工夫内能够获取尽可能高的收集效率。
  • 相比于 CMSGC,G1 未必能做到 CMS 在最好状况下的延时进展,然而最差状况要好很多。

(2)G1 垃圾收集器的毛病

相较于 CMS,G1 还不具备全方位、压倒性劣势。比方在用户程序运行过程中,G1 无论是为了垃圾收集产生的内存占用(Footprint)还是程序运行时的额定执行负载(overload)都要比 CMS 要高。

从教训上来说,在小内存利用上 CMS 的体现大概率会优于 G1,而 G1 在大内存利用上则施展其劣势。平衡点在 6 -8GB 之间。

7.3 G1 参数设置

  • -XX:+UseG1GC:手动指定应用 G1 垃圾收集器执行内存回收工作
  • -XX:G1HeapRegionSize:设置每个 Region 的大小。值是 2 的幂,范畴是 1MB 到 32MB 之间,指标是依据最小的 Java 堆大小划分出约 2048 个区域。默认是堆内存的 1 /2000。
  • -XX:MaxGCPauseMillis:设置冀望达到的最大 GC 进展工夫指标(JVM 会尽力实现,但不保障达到)。默认值是 200ms。
  • -XX:+ParallelGcThread:设置 STW 工作线程数的值。最多设置为 8。
  • -XX:ConcGCThreads:设置并发标记的线程数。将 n 设置为并行垃圾回收线程数(ParallelGcThreads)的 1 / 4 左右。
  • -XX:InitiatingHeapOccupancyPercent:设置触发并发 GC 周期的 Java 堆占用率阈值。超过此值,就触发 GC。默认值是 45。

G1 收集器的常见操作步骤

G1 的设计准则就是简化 JVM 性能调优,开发人员只须要简略的三步即可实现调优:

  • 第一步:开启 G1 垃圾收集器
  • 第二步:设置堆的最大内存
  • 第三步:设置最大的进展工夫

G1 中提供了三种垃圾回收模式:YoungGC、Mixed GC 和 Fu11GC,在不同的条件下被触发。

7.4 G1 收集器的实用场景

面向服务端利用,针对具备大内存、多处理器的机器。(在一般大小的堆里体现并不惊喜)

最次要的利用是须要低 GC 提早,并具备大堆的应用程序提供解决方案;

如:在堆大小约 6GB 或更大时,可预测的暂停工夫能够低于 e.5 秒;(G1 通过每次只清理一部分而不是全副的 Region 的增量式清理来保障每次 Gc 进展工夫不会过长)。
用来替换掉 JDK1.5 中的 CMS 收集器;在上面的状况时,应用 61 可能比 CMS 好:

  • 超过 5e% 的 Java 堆被流动数据占用;
  • 对象调配频率或年代晋升频率变化很大;
  • GC 进展工夫过长(长于 e.5 至 1 秒)

HotSpot 垃圾收集器里,除了 61 以外,其余的垃圾收集器应用内置的 JVM 线程执行 Gc 的多线程操作,而 G1GC 能够采纳利用线程承当后盾运行的 GC 工作,即当 JVM 的 GC 线程处理速度慢时,零碎会调用应用程序线程帮忙减速垃圾回收过程。

7.5 分区 Region:化整为零

应用 G1 收集器时,它将整个 Java 堆划分成约 2048 个大小雷同的独立 Region 块,每个 Region 块大小依据堆空间的理论大小而定,整体被管制在 1MB 到 32MB 之间,且为 2 的 N 次幂,即 1MB,2MB,4MB,8MB,16MB,32MB。能够通过

XX:G1HeapRegionsize 设定。所有的 Region 大小雷同,且在 JVM 生命周期内不会被扭转

尽管还保留有新生代和老年代的概念,但新生代和老年代不再是物理隔离的了,它们都是一部分 Region(不须要间断)的汇合。通过 Region 的动态分配形式实现逻辑上的间断。

一个 region 有可能属于 Eden,Survivor 或者 old/Tenured 内存区域。然而一个 region 只可能属于一个角色。图中的 E 示意该 region 属于 Eden 内存区域,s 示意属于 survivor 内存区域,o 示意属于 01d 内存区域。图中空白的示意未应用的内存空间。

G1 垃圾收集器还减少了一种新的内存区域,叫做 Humongous 内存区域,如图中的 H 块。次要用于存储大对象,如果超过 1.5 个 region,就放到 H。

设置 H 的起因:对于堆中的对象,默认间接会被调配到老年代,然而如果它是一个短期存在的大对象就会对垃圾收集器造成负面影响。为了解决这个问题,G1 划分了一个 Humongous 区,它用来专门寄存大对象。如果一个 H 区装不下一个大对象,那么 G1 会寻找间断的 H 区来存储。为了能找到间断的 H 区,有时候不得不启动 Full GC。G1 的大多数行为都把 H 区作为老年代的一部分来对待。

每个 Region 都是通过指针碰撞来调配空间

7.6 Remembered Set(记忆集)

一个 Region 不可能是孤立的,一个 Region 中的对象可能被其余任意 Region 中对象援用,判断对象存活时,是否须要扫描整个 Java 堆能力保障精确?

在其余的分代收集器,也存在这样的问题(而 G1 更突出)回收新生代也不得不同时扫描老年代?这样会升高 MinorGC 的效率。

解决办法:

无论 G1 还是其余分代收集器,JVM 都是应用 Remembered Set 来防止全局扫描;

每个 Region 都有一个对应的 Remembered Set

每次 Reference 类型数据写操作时,都会产生一个 Write Barrier 临时中断操作;

而后查看将要写入的援用指向的对象是否和该 Reference 类型数据在不同的 Region(其余收集器:查看老年代对象是否援用了新生代对象);

如果不同,通过 CardTable 把相干援用信息记录到援用指向对象的所在 Region 对应的 Remembered Set 中;

当进行垃圾收集时,在 GC 根节点的枚举范畴退出 Remembered Set;就能够保障不进行全局扫描,也不会有脱漏。

7.7 G1 垃圾回收器的回收过程

G1 GC 的垃圾回收过程次要包含如下三个环节:

  • 年老代 GC(Young GC)
  • 老年代并发标记过程(Concurrent Mark)
  • 混合回收(Mixed GC)

(如果须要,单线程、独占式、高强度的 Full GC 还是持续存在的。它针对 GC 的评估失败提供了一种失败爱护机制,即强力回收。)

顺时针,YOUNG GC->YOUNG GC+Concurrent Mark->Mixed GC 程序,进行垃圾回收。

应用程序分配内存,当年老代的 Eden 区用尽时开始年老代回收过程;G1 的年老代收集阶段是一个并行的独占式收集器。在年老代回收期,G1 GC 暂停所有应用程序线程,启动多线程执行年老代回收。而后从年老代区间挪动存活对象到 Survivor 区间或者老年区间,也有可能是两个区间都会波及。

当堆内存应用达到肯定值(默认 45%)时,开始老年代并发标记过程。

标记实现马上开始混合回收过程。对于一个混合回收期,G1GC 从老年区间挪动存活对象到闲暇区间,这些闲暇区间也就成为了老年代的一部分。和年老代不同,老年代的 G1 回收器和其余 GC 不同,G1 的老年代回收器不须要整个老年代被回收,一次只须要扫描 / 回收一小部分老年代的 Region 就能够了。同时,这个老年代 Region 是和年老代一起被回收的。

举个例子:一个 Web 服务器,Java 过程最大堆内存为 4G,每分钟响应 1500 个申请,每 45 秒钟会新调配大概 2G 的内存。G1 会每 45 秒钟进行一次年老代回收,每 31 个小时整个堆的使用率会达到 45%,会开始老年代并发标记过程,标记实现后开始四到五次的混合回收。

(1)年老代 GC

JVM 启动时,G1 先筹备好 Eden 区,程序在运行过程中一直创建对象到 Eden 区,当 Eden 空间耗尽时,G1 会启动一次年老代垃圾回收过程。

YGC 时,首先 G1 进行应用程序的执行(Stop-The-World),G1 创立回收集(Collection Set),回收集是指须要被回收的内存分段的汇合,年老代回收过程的回收集蕴含年老代 Eden 区Survivor 区 所有的内存分段。

而后开始如下回收过程:

  • 扫描根

根是指 static 变量指向的对象,正在执行的办法调用链条上的局部变量等。根援用连同 RSet 记录的内部援用作为扫描存活对象的入口。

  • 更新 RSet

解决 dirty card queue(见备注)中的 card,更新 RSet。此阶段实现后,RSet 能够精确的反映老年代对所在的内存分段中对象的援用

  • 解决 RSet

辨认被老年代对象指向的 Eden 中的对象,这些被指向的 Eden 中的对象被认为是存活的对象。

  • 复制对象。

此阶段,对象树被遍历,Eden 区内存段中存活的对象会被复制到 Survivor 区中空的内存分段,Survivor 区内存段中存活的对象如果年龄未达阈值,年龄会加 1,达到阀值会被会被复制到 o1d 区中空的内存分段。如果 Survivor 空间不够,Eden 空间的局部数据会间接降职到老年代空间。

  • 解决援用

解决 Soft,Weak,Phantom,Final,JNI Weak 等援用。最终 Eden 空间的数据为空,GC 进行工作,而指标内存中的对象都是间断存储的,没有碎片,所以复制过程能够达到内存整理的成果,缩小碎片。

(2)并发标记过程

  • 初始标记阶段:标记从根节点间接可达的对象。这个阶段是 STW 的,并且会触发一次年老代 GC。
  • 根区域扫描(Root Region Scanning):G1 GC 扫描 survivor 区间接可达的老年代区域对象,并标记被援用的对象。这一过程必须在 youngGC 之前实现。
  • 并发标记(Concurrent Marking):在整个堆中进行并发标记(和应用程序并发执行),此过程可能被 youngGC 中断。在并发标记阶段,若发现区域对象中的所有对象都是垃圾,那这个区域会被立刻回收。同时,并发标记过程中,会计算每个区域的对象活性(区域中存活对象的比例)。
  • 再次标记(Remark):因为应用程序继续进行,须要修改上一次的标记后果。是 STW 的。G1 中采纳了比 CMS 更快的初始快照算法:snapshot-at-the-beginning(SATB)。
  • 独占清理(cleanup,STW):计算各个区域的存活对象和 GC 回收比例,并进行排序,辨认能够混合回收的区域。为下阶段做铺垫。是 STW 的。这个阶段并不会实际上去做垃圾的收集
  • 并发清理阶段:辨认并清理齐全闲暇的区域。

(3)混合回收

当越来越多的对象降职到老年代 Old region 时,为了防止堆内存被耗尽,虚构机会触发一个混合的垃圾收集器,即 Mixed GC,该算法并不是一个 Old GC,除了回收整个 Young Region,还会回收一部分的 Old Region。这里须要留神:是一部分老年代,而不是全副老年代。能够抉择哪些 Old Region 进行收集,从而能够对垃圾回收的耗时工夫进行管制。也要留神的是 Mixed GC 并不是 Full GC。

并发标记完结当前,老年代中百分百为垃圾的内存分段被回收了,局部为垃圾的内存分段被计算了进去。默认状况下,这些老年代的内存分段会分 8 次(能够通过 -XX:G1MixedGCCountTarget 设置)被回收。

混合回收的回收集(Collection Set)包含八分之一的老年代内存分段,Eden 区内存分段,Survivor 区内存分段。混合回收的算法和年老代回收的算法齐全一样,只是回收集多了老年代的内存分段。具体过程请参考下面的年老代回收过程。

因为老年代中的内存分段默认分 8 次回收,G1 会优先回收垃圾多的内存分段。垃圾占内存分段比例越高的,越会被先回收。并且有一个阈值会决定内存分段是否被回收,

-XX:G1MixedGCLiveThresholdPercent,默认为 65%,意思是垃圾占内存分段比例要达到 65% 才会被回收。如果垃圾占比太低,意味着存活的对象占比高,在复制的时候会破费更多的工夫。

混合回收并不一定要进行 8 次。有一个阈值-XX:G1HeapWastePercent,默认值为 10%,意思是容许整个堆内存中有 10% 的空间被节约,意味着如果发现能够回收的垃圾占堆内存的比例低于 10%,则不再进行混合回收。因为 GC 会破费很多的工夫然而回收到的内存却很少。

(4)Full GC

G1 的初衷就是要防止 Full GC 的呈现。然而如果上述形式不能失常工作,G1 会进行应用程序的执行(Stop-The-World),应用 b 的内存回收算法进行垃圾回收,性能会十分差,应用程序进展工夫会很长。

要防止 Full GC 的产生,一旦产生须要进行调整。什么时候会产生 Full GC 呢?比方堆内存太小,当 G1 在复制存活对象的时候没有空的内存分段可用,则会回退到 Full GC,这种状况能够通过增大内存解决。
导致 G1Full GC 的起因可能有两个:

  • Evacuation 的时候没有足够的 to-space 来寄存降职的对象;
  • 并发处理过程实现之前空间耗尽。

7.8 G1 回收的优化倡议

从 oracle 官网走漏进去的信息可获知,回收阶段(Evacuation)其实本也有想过设计成与用户程序一起并发执行,但这件事件做起来比较复杂,思考到 G1 只是回一部分 Region,进展工夫是用户可管制的,所以并不迫切去实现,而抉择把这个个性放到了 G1 之后呈现的低提早垃圾收集器(即 ZGC)中。另外,还思考到 G1 不是仅仅面向低提早,进展用户线程可能最大幅度提高垃圾收集效率,为了保障吞吐量所以才抉择了齐全暂停用户线程的实现计划。

年老代大小

  • 防止应用 -Xmn 或 -XX:NewRatio 等相干选项显式设置年老代大小
  • 固定年老代的大小会笼罩

暂停工夫指标暂停工夫指标不要太过严苛

  • G1 GC 的吞吐量指标是 90% 的应用程序工夫和 10% 的垃圾回收工夫
  • 评估 G1GC 的吞吐量时,暂停工夫指标不要太严苛。指标太过严苛示意你违心接受更多的垃圾回收开销,而这些会间接影响到吞吐量。

八、垃圾回收器总结

截止 JDK1.8,一共有 7 款不同的垃圾收集器。每一款的垃圾收集器都有不同的特点,在具体应用的时候,须要依据具体的状况选用不同的垃圾收集器。

GC 倒退阶段:Serial=> Parallel(并行)=> CMS(并发)=> G1 => ZGC

不同厂商、不同版本的虚拟机实现差距比拟大。HotSpot 虚拟机在 JDK7/ 8 后所有收集器及组合如下图

怎么抉择垃圾回收器

Java 垃圾收集器的配置对于 JVM 优化来说是一个很重要的抉择,抉择适合的垃圾收集器能够让 JVM 的性能有一个很大的晋升。怎么抉择垃圾收集器?

  • 优先调整堆的大小让 JVM 自适应实现。
  • 如果内存小于 100M,应用串行收集器。
  • 如果是单核、单机程序,并且没有进展工夫的要求,串行收集器。
  • 如果是多 CPU、须要高吞吐量、容许进展工夫超过 1 秒,抉择并行或者 JVM 本人抉择。
  • 如果是多 CPU、谋求低进展工夫,需疾速响应(比方提早不能超过 1 秒,如互联网利用),应用并发收集器。
  • 官网举荐 G1,性能高。当初互联网的我的项目,根本都是应用 G1。

最初须要明确一个观点:

  • 没有最好的收集器,更没有万能的收集器。
  • 调优永远是针对特定场景、特定需要,不存在一劳永逸的收集器。

九、GC 日志剖析

通过浏览 GC 日志,咱们能够理解 Java 虚拟机内存调配与回收策略。
内存调配与垃圾回收的参数列表

  • -XX:+PrintGC输入 GC 日志。相似:-verbose:gc
  • -XX:+PrintGcDetails输入 GC 的具体日志
  • -XX:+PrintGcTimestamps 输入 GC 的工夫戳(以基准工夫的模式)
  • -XX:+PrintGCDatestamps 输入 GC 工夫戳(以日期的模式,如 2013-05-04T21:53:59.234+0800)
  • -XX:+PrintHeapAtGC在进行 GC 的前后打印出堆的信息
  • -Xloggc:../logs/gc.1og 日志文件的输入门路

verbose:gc

关上 GC 日志

-verbose:gc

这个只会显示总的 GC 堆的变动,如下:

参数解析

PrintGCDetails

关上 GC 日志

-verbose:gc -XX:+PrintGCDetails

输出信息如下

参数解析

补充

  • [GC” 和 ”[Full GC” 阐明了这次垃圾收集的进展类型,如果有 ”Full” 则阐明 GC 产生了 ”stop The World”。
  • 应用 Serial 收集器在新生代的名字是 Default New Generation,因而显示的是 ”[DefNew”。
  • 应用 ParNew 收集器在新生代的名字会变成 ”[ParNew”,意思是 ”Parallel New Generation”。
  • 应用 Parallel scavenge 收集器在新生代的名字是”[PSYoungGen”。
  • 老年代的收集和新生代情理一样,名字也是收集器决定的。
  • 应用 G1 收集器的话,会显示为 ”garbage-first heap”。

Allocation Failure 表明本次引起 GC 的起因是因为在年老代中没有足够的空间可能存储新的数据了。

[PSYoungGen:5986K->696K(8704K)]5986K->704K(9216K)中括号内:GC 回收前年老代大小,回收后大小,(年老代总大小)括号外:GC 回收前年老代和老年代大小,回收后大小,(年老代和老年代总大小)

user 代表用户态回收耗时,sys 内核态回收耗时,rea 理论耗时。因为多核的起因,工夫总和可能会超过 real 工夫

Young GC 图片

Full GC 图片

GC 回收举例

public class GCUseTest {
    static final Integer _1MB = 1024 * 1024;
    public static void main(String[] args) {byte [] allocation1, allocation2, allocation3, allocation4;
        allocation1 = new byte[2 *_1MB];
        allocation2 = new byte[2 *_1MB];
        allocation3 = new byte[2 *_1MB];
        allocation4 = new byte[4 *_1MB];
    }
}

咱们设置 JVM 启动参数

-Xms10m -Xmx10m -XX:+PrintGCDetails

首先咱们会将 3 个 2M 的数组寄存到 Eden 区,而后前面 4M 的数组来了后,将无奈存储,因为 Eden 区只剩下 2M 的残余空间了,那么将会进行一次 Young GC 操作,将原来 Eden 区的内容,寄存到 Survivor 区,然而 Survivor 区也寄存不下,那么就会间接升级存入 Old 区。

而后咱们将 4M 对象存入到 Eden 区中:

罕用的日志剖析工具有:GCViewer、GCEasy、GCHisto、GCLogViewer、Hpjmeter、garbagecat 等。

十、垃圾回收器的新倒退

GC 依然处于飞速发展之中,目前的默认选项 G1 GC 在一直的进行改良,很多咱们原来认为的毛病,例如串行的 Full GC、Card Table 扫描的低效等,都曾经被大幅改良,例如,JDK10 当前,Fu11GC 曾经是并行运行,在很多场景下,其体现还略优于 ParallelGC 的并行 Full GC 实现。

即便是 Serial GC,尽管比拟古老,然而简略的设计和实现未必就是过期的,它自身的开销,不论是 GC 相干数据结构的开销,还是线程的开销,都是十分小的,所以随着云计算的衰亡,在 serverless 等新的利用场景下,Serial GC 找到了新的舞台。

比拟可怜的是 CMS GC,因为其算法的实践缺点等起因,尽管当初还有十分大的用户群体,但在 JDK9 中曾经被标记为废除,并在 JDK14 版本中移除。

Epsilon:A No-Op GarbageCollector(Epsilon 垃圾回收器,”No-Op(无操作)” 回收器)

ZGC:A Scalable Low-Latency Garbage Collector(Experimental)(ZGC:可伸缩的低提早垃圾回收器,处于实验性阶段)

当初 G1 回收器已成为默认回收器好几年了。咱们还看到了引入了两个新的收集器:ZGC(JDK11 呈现)和 Shenandoah(Open JDK12)

主打特点:低进展工夫

Open JDK12 的 Shenandoah GC

Open JDK12 的 Shenandoah GC:低进展工夫的 GC(实验性)

Shenandoah,无疑是泛滥 GC 中最孤单的一个。是第一款不禁 Oracle 公司团队领导开发的 Hotspot 垃圾收集器。不可避免的受到官网的排斥。比方号称 openJDK 和 OracleJDk 没有区别的 Oracle 公司仍回绝在 OracleJDK12 中反对 Shenandoah。

Shenandoah 垃圾回收器最后由 RedHat 进行的一项垃圾收集器钻研我的项目 Pauseless GC 的实现,旨在针对 JVM 上的内存回收实现低进展的需要。在 2014 年奉献给 OpenJDK。

Red Hat 研发 Shenandoah 团队对外声称,Shenandoah 垃圾回收器的暂停工夫与堆大小无关,这意味着无论将堆设置为 200MB 还是 200GB,99.9% 的指标都能够把垃圾收集的进展工夫限度在十毫秒以内。不过理论使用性能将取决于理论工作堆的大小和工作负载。

这是 RedHat 在 2016 年发表的论文数据,测试内容是应用 Es 对 200GB 的维基百科数据进行索引。从后果看:

进展工夫比其余几款收集器的确有了质的飞跃,但也未实现最大进展工夫管制在十毫秒以内的指标。
而吞吐量方面呈现了显著的降落,总运行工夫是所有测试收集器里最长的。

总结

  • shenandoah GC 的弱项:高运行累赘下的吞吐量降落。
  • shenandoah GC 的强项:低延迟时间。

革命性的 ZGC

ZGC 与 Shenandoah 指标高度类似,在尽可能对吞吐量影响不大的前提下,实现在任意堆内存大小下都能够把垃圾收集的停颇工夫限度在十毫秒以内的低提早。

《深刻了解 Java 虚拟机》一书中这样定义 ZGC:ZGC 收集器是一款基于 Region 内存布局的,(临时)不设分代的,应用了读屏障、染色指针和内存多重映射等技术来实现可并发的标记 - 压缩算法的,以低提早为首要指标的一款垃圾收集器。

ZGC 的工作过程能够分为 4 个阶段:并发标记 – 并发准备重调配 – 并发重调配 – 并发重映射 等。

ZGC 简直在所有中央并发执行的,除了初始标记的是 STW 的。所以进展工夫简直就消耗在初始标记上,这部分的理论工夫是非常少的。

进展工夫比照

尽管 ZGC 还在试验状态,没有实现所有个性,但此时性能曾经相当亮眼,用“令人震惊、革命性”来形容,不为过。
将来将在服务端、大内存、低提早利用的首选垃圾收集器。

JDK14 之前,ZGC 仅 Linux 才反对。

只管许多应用 ZGc 的用户都应用类 Linux 的环境,但在 Windows 和 macos 上,人们也须要 ZGC 进行开发部署和测试。许多桌面利用也能够从 ZGC 中受害。因而,ZGC 个性被移植到了 Windows 和 macos 上。

当初 mac 或 Windows 上也能应用 ZGC 了,示例如下:

-XX:+UnlockExperimentalVMOptions -XX:+UseZGC

正文完
 0