zoukankan      html  css  js  c++  java
  • 【JVM进阶之路】七:垃圾收集器盘点

    在前面,我们已经了解了JVM的分代收集,知道JVM垃圾收集在新生代主要采用标记-复制算法,在老年代主要采用标记-清除标记-整理算法。接下来,我们看一看JDK默认虚拟机HotSpot的一些垃圾收集器的实现。

    1、常见垃圾回收器

    首先来看一下JDK 11之前全部可用的垃圾收集器。

    HotSpot虚拟机

    图中列出了七种垃圾收集器,连线表示可以配合使用,所在区域表示它是属于新生代收集器或是老年代收集器。

    这里还标出了垃圾收集器采用的收集算法,G1收集器比较特殊,整体采用标记-整理算法,局部采用标记-复制算法,后面再细讲。

    1.1、Serial收集器

    Serial收集器是最基础、历史最悠久的收集器。

    如同它的名字(串行),它是一个单线程工作的收集器,使用一个处理器或一条收集线程去完成垃圾收集工作。并且进行垃圾收集时,必须暂停其他所有工作线程,直到垃圾收集结束——这就是所谓的“Stop The World”。

    Serial/Serial Old收集器的运行过程如图:

    Serial/Serial Old收集器运行示意图

    1.2、ParNew收集器

    ParNew收集器实质上是Serial收集器的多线程并行版本,使用多条线程进行垃圾收集。

    ParNew收集器的工作过程如图所示:

    ParNew/Serial Old收集器运行示意图

    这里值得一提的是Par是Parallel(并行)的缩写,但需要注意的是,这个并行(Parallel)仅仅是描述同一时间多条GC线程协同工作,而不是GC线程和用户线程同时运行。ParNew垃圾收集也是需要Stop The World的。

    1.3、Parallel Scavenge收集器

    Parallel Scavenge收集器是一款新生代收集器,基于标记-复制算法实现,也能够并行收集。和ParNew有些类似,但Parallel Scavenge主要关注的是垃圾收集的吞吐量。

    所谓吞吐量指的是运行用户代码的时间与处理器总消耗时间的比值。这个比例越高,证明垃圾收集占整个程序运行的比例越小。

    吞吐量

    Parallel Scavenge收集器提供了两个参数用于精确控制吞吐量:

    • -XX:MaxGCPauseMillis,最大垃圾回收停顿时间。这个参数的原理是空间换时间,收集器会控制新生代的区域大小,从而尽可能保证回收少于这个最大停顿时间。简单的说就是回收的区域越小,那么耗费的时间也越小。
      所以这个参数并不是设置得越小越好。设太小的话,新生代空间会太小,从而更频繁的触发GC。

    • -XX:GCTimeRatio,垃圾收集时间与总时间占比。这个是吞吐量的倒数,原理和MaxGCPauseMillis相同。

    由于与吞吐量关系密切,Parallel Scavenge收集器也经常被称作“吞吐量优先收集器”。

    1.4、Serial Old收集器

    Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器,使用标记-整理算法。

    Serial Old收集器的工作过程如图:

    Serial/Serial Old收集器运行示意图

    1.5、Parallel Old收集器

    Parallel Old是Parallel Scavenge收集器的老年代版本,支持多线程并发收集,基于标记-整理算法实现。

    Parallel Scavenge/Parallel Old收集器运行示意图

    1.6、CMS收集器

    CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间为目标的收集器,同样是老年代的收集齐,采用标记-清除算法。

    CMS收集齐的垃圾收集分为四步:

    • 初始标记(CMS initial mark):单线程运行,需要Stop The World,标记GC Roots能直达的对象。
    • 并发标记((CMS concurrent mark):无停顿,和用户线程同时运行,从GC Roots直达对象开始遍历整个对象图。
    • 重新标记(CMS remark):多线程运行,需要Stop The World,标记并发标记阶段产生对象。
    • 并发清除(CMS concurrent sweep):无停顿,和用户线程同时运行,清理掉标记阶段标记的死亡的对象。

    涉及到了多次标记的过程,这里插入一点三色抽象的知识。三色抽象用来描述对象在垃圾收集过程中的状态。

    通常白色代表对象未被扫描到,灰色表示对象被扫描到但未被处理,黑色表示对象及其后代已被处理。在CMS的标记和清除过程中就用到了这种抽象,详细的可以查看参考【5】。

    Concurrent Mark Sweep收集器运行示意图如下:

    Concurrent Mark Sweep收集器运行示意图

    优点:CMS最主要的优点在名字上已经体现出来——并发收集、低停顿。

    缺点:CMS同样有三个明显的缺点。

    • Mark Sweep算法会导致内存碎片比较多

    • CMS的并发能力比较依赖于CPU资源,并发回收时垃圾收集线程可能会抢占用户线程的资源,导致用户程序性能下降。

    • 并发清除阶段,用户线程依然在运行,会产生所谓的理“浮动垃圾”(Floating Garbage),本次垃圾收集无法处理浮动垃圾,必须到下一次垃圾收集才能处理。如果浮动垃圾太多,会触发新的垃圾回收,导致性能降低。

    1.7、Garbage First收集器

    Garbage First(简称G1)收集器是垃圾收集器的一个颠覆性的产物,它开创了局部收集的设计思路和基于Region的内存布局形式。

    虽然G1也仍是遵循分代收集理论设计的,但其堆内存的布局与其他收集器有非常明显的差异。以前的收集器分代是划分新生代、老年代、持久代等。

    垃圾分代区域

    G1把连续的Java堆划分为多个大小相等的独立区域(Region),每一个Region都可以根据需要,扮演新生代的Eden空间、Survivor空间,或者老年代空间。收集器能够对扮演不同角色的Region采用不同的策略去处理。

    G1 Heap Regions

    这样就避免了收集整个堆,而是按照若干个Region集进行收集,同时维护一个优先级列表,跟踪各个Region回收的“价值,优先收集价值高的Region。

    G1收集器的运行过程大致可划分为以下四个步骤:

    • 初始标记(initial mark),标记了从GC Root开始直接关联可达的对象。STW(Stop the World)执行。

    • 并发标记(concurrent marking),和用户线程并发执行,从GC Root开始对堆中对象进行可达性分析,递归扫描整个堆里的对象图,找出要回收的对象、

    • 最终标记(Remark),STW,标记再并发标记过程中产生的垃圾。

    • 筛选回收(Live Data Counting And Evacuation),制定回收计划,选择多个Region 构成回收集,把回收集中Region的存活对象复制到空的Region中,再清理掉整个旧 Region的全部空间。需要STW。

    G1收集器运行示意图

    相比CMS,G1的优点有很多,可以指定最大停顿时间、分Region的内存布局、按收益动态确定回收集。

    只从内存的角度来看,与CMS的“标记-清除”算法不同,G1从整体来看是基于“标记-整理”算法实现的收集器,但从局部(两个Region 之间)上看又是基于“标记-复制”算法实现,无论如何,这两种算法都意味着G1运作期间不会产生内存空间碎片,垃圾收集完成之后能提供规整的可用内存。

    2、前沿垃圾回收器

    2.1、ZGC收集器

    在JDK 11当中,加入了实验性质的ZGC。它的回收耗时平均不到2毫秒。它是一款低停顿高并发的收集器。

    与CMS中的ParNew和G1类似,ZGC也采用标记-复制算法,不过ZGC对该算法做了重大改进:ZGC在标记、转移和重定位阶段几乎都是并发的,这是ZGC实现停顿时间小于10ms目标的最关键原因。

    ZGC垃圾回收周期

    ZGC虽然在JDK 11还处于实验阶段,但由于算法与思想是一个非常大的提升,未来前景相信还是很广阔的。

    3、垃圾收集器选择

    3.1、收集器选择权衡

    垃圾收集器的选择需要权衡的点还是比较多的——例如运行应用的基础设施如何?使用JDK的发行商是什么?等等……

    这里简单地列一下上面提到的一些收集器的适用场景:

    • Serial :如果应用程序有一个很小的内存空间(大约100 MB)亦或它在没有停顿时间要求的单线程处理器上运行。
    • Parallel:如果优先考虑应用程序的峰值性能,并且没有时间要求要求,或者可以接受1秒或更长的停顿时间。
    • CMS/G1:如果响应时间比吞吐量优先级高,亦或垃圾收集暂停必须保持在大约1秒以内。
    • ZGC:如果响应时间是高优先级的,亦或堆空间比较大。

    3.1、设置垃圾收集器

    设置垃圾收集器(组合)的参数如下:

    新生代 老年代 JVM 参数
    Incremental Incremental -Xincgc
    Serial Serial -XX:+UseSerialGC
    Parallel Scavenge Serial -XX:+UseParallelGC -XX:-UseParallelOldGC
    Parallel New Serial N/A
    Serial Parallel Old N/A
    Parallel Scavenge Parallel Old -XX:+UseParallelGC -XX:+UseParallelOldGC
    Parallel New Parallel Old N/A
    Serial CMS -XX:-UseParNewGC -XX:+UseConcMarkSweepGC
    Parallel Scavenge CMS N/A
    Parallel New CMS -XX:+UseParNewGC -XX:+UseConcMarkSweepGC
    G1 -XX:+UseG1GC

    参考:

    【1】:周志朋编著《深入理解Java虚拟机:JVM高级特性与最佳实践》

    【2】:《垃圾回收算法手册 自动内存管理的艺术》

    【3】:Garbage Collection in Java – What is GC and How it Works in the JVM

    【4】:Java Hotspot G1 GC的一些关键技术

    【5】:GC Algorithms: Implementations

    【6】:新一代垃圾回收器ZGC的探索与实践

  • 相关阅读:
    UVA 11925 Generating Permutations 生成排列 (序列)
    UVA 1611 Crane 起重机 (子问题)
    UVA 11572 Unique snowflakes (滑窗)
    UVA 177 PaperFolding 折纸痕 (分形,递归)
    UVA 11491 Erasing and Winning 奖品的价值 (贪心)
    UVA1610 PartyGame 聚会游戏(细节题)
    UVA 1149 Bin Packing 装箱(贪心)
    topcpder SRM 664 div2 A,B,C BearCheats , BearPlays equalPiles , BearSorts (映射)
    UVA 1442 Cave 洞穴 (贪心+扫描)
    UVA 1609 Foul Play 不公平竞赛 (构(luan)造(gao)+递归)
  • 原文地址:https://www.cnblogs.com/three-fighter/p/14599218.html
Copyright © 2011-2022 走看看