HotSpot虚拟机中的7种垃圾回收器

垃圾收集器概述

垃圾收集器组合

  • 图中展示了7种垃圾收集器,Serial、ParNew、Parallel Scavenge、CMS、Serial old、Parallel Old、G1
  • 带有连线的收集器代表可以组合使用
  • Serial Old作为CMS出现"Concurrent Mode Failure"失败的后备预案

并发垃圾收集和并行垃圾收集的区别

  • 并行指多条垃圾收集线程并行执行,但是用户线程需要等待,即进入stop the world状态,对应的垃圾收集器有ParNew、Parallel Old、Parallel Scavenge。
  • 并发指用户线程和垃圾收集线程同时执行,不一定是同时执行,可能是交替执行,对应的垃圾收集器有CMS、G1。

Minor GC和Full GC

Minor GC表示新生代垃圾收集,Full GC表示新生代和老年代垃圾收集,Major GC表示老年代垃圾收集。

Serial收集器

  1. 特点
  • 针对新生代、采用复制算法、单线程收集
  • 进行垃圾收集是会暂停所有的工作线程
    Serial/Serial Old组合收集器运行示意图如下:
  1. 应用场景
  • HotSpot在client模式下默认的新生代垃圾收集器
  • 对于限定单个CPU的环境来说,Serial收集器没有线程交互(切换)开销,可以获得最高的单线程收集效率
  1. 设置参数
  • “-XX:+UseSerialGC”:添加该参数来显式的使用串行垃圾收集器。

ParNew收集器

  1. 特点
  • 针对新生代、采用复制算法、多线程收集
  • 进行垃圾收集时会停止所有的用户线程
    ParNew/Serial Old组合收集器运行示意图如下:
  1. 应用场景
  • 在Server模式下,ParNew收集器是一个非常重要的新生代收集器,因为除Serial外,目前只有它能与CMS收集器配合工作。
  • 但在单个CPU环境中,不会比Serail收集器有更好的效果,因为存在线程交互开销。
  1. 设置参数
  • “-XX:+UseConcMarkSweepGC”:指定使用CMS后,会默认使用ParNew作为新生代收集器
  • “-XX:+UseParNewGC”:强制指定使用ParNew
  • “-XX:ParallelGCThreads”:指定垃圾收集的线程数量,ParNew默认开启的收集线程与CPU的数量相同
  1. 为什么只有ParNew能与CMS收集器配合?
  • CMS是HotSpot在JDK1.5推出的第一款真正意义上的并发(Concurrent)收集器,第一次实现了让垃圾收集线程与用户线程(基本上)同时工作;CMS作为老年代收集器,但却无法与JDK1.4已经存在的新生代收集器Parallel Scavenge配合工作;因为Parallel Scavenge(以及G1)都没有使用传统的GC收集器代码框架,而另外独立实现;而其余几种收集器则共用了部分的框架代码;

Parallel Scavenge收集器

  1. 特点
  • 针对新生代,采用复制算法,多线程收集
  • CMS等收集器的关注点是尽可能地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达一个可控制的吞吐量(Throughput)
  1. 应用场景
  • 以高吞吐量为目标,即减少垃圾收集时间,让用户代码获得更长的运行时间(减少垃圾收集时间并不意味着减少用户线程停顿时间)
  • 当应用程序运行在具有多个CPU上,对暂停时间没有特别高的要求时,即程序主要在后台进行计算,而不需要与用户进行太多交互
  1. 设置参数
  • “-XX:MaxGCPauseMillis”:控制最大垃圾收集停顿时间, MaxGCPauseMillis设置得稍小,停顿时间可能会缩短,但也可能会使得吞吐量下降,因为可能导致垃圾收集发生得更频繁。
  • “-XX:GCTimeRatio”:设置垃圾收集时间占总时间的比率,0<n<100的整数,GCTimeRatio相当于设置吞吐量大小,
  • “-XX:+UseAdptiveSizePolicy”: 开启这个参数后,就不用手工指定一些细节参数,如新生代的大小(-Xmn)、Eden与Survivor区的比例(-XX:SurvivorRation)、晋升老年代的对象年龄(-XX:PretenureSizeThreshold)等,JVM会根据当前系统运行情况收集性能监控信息,动态调整这些参数,以提供最合适的停顿时间或最大的吞吐量,这种调节方式称为GC自适应的调节策略(GC Ergonomiscs)。
  1. 吞吐量
  • 吞吐量=运行用户代码时间/(运行用户代码时间+垃圾收集时间)

Serial Old收集器

  1. 特点
  • 针对老年代,采用标记整理算法,单线程收集
    Serial/Serial Old收集器运行示意图如下:
  1. 应用场景
  • 主要用于Client模式
  • 而在Server模式有两大用途:(1)在JDK1.5及之前,与Parallel Scavenge收集器搭配使用(JDK1.6有Parallel Old收集器可搭配)(2)作为CMS收集器的后备预案,在并发收集发生Concurrent Mode Failure时使用

Parallel Old收集器

  1. 特点
  • 针对老年代,采用标记整理算法,多线程收集
    Parallel Scavenge/Parallel Old收集器运行示意图如下:
  1. 应用场景
  • JDK1.6及之后用来代替老年代的Serial Old收集器,特别是在Server模式,多CPU的情况下。
  1. 设置参数
  • “-XX:+UseParallelOldGC”:指定使用Parallel Old收集器

CMS收集器

并发标记清理(Concurrent Mark Sweep,CMS)收集器也称为并发低停顿收集器(Concurrent Low Pause Collector)或低延迟(low-latency)垃圾收集器。

  1. 特点
  • 针对老年代,采用标记清除算法,以获取最短停顿时间为目标,并发收集,需要更多内存。
  1. 应用场景
  • 与用户交互较多的场景,希望系统停顿时间最短,注重服务的响应速度, 如常见WEB、B/S系统的服务器上的应用。
  1. 设置参数
  • “-XX:+UseConcMarkSweepGC”:指定使用CMS收集器
  1. CMS收集器运作过程
    (1)初始标记(CMS initial mark),仅标记一下GC Roots能直接关联到的对象,速度很快,但需要"Stop The World"。
    (2)并发标记(CMS concurrent mark),进行GC Roots Tracing的过程,刚才产生的集合中标记出存活对象,应用程序也在运行,并不能保证可以标记出所有的存活对象。
    (3)重新标记(CMS remark),为了修正并发标记期间因用户程序继续运作而导致标记变动的那一部分对象的标记记录,需要"Stop The World",且停顿时间比初始标记稍长,但远比并发标记短,采用多线程并行执行来提升效率。
    (4)并发清除(CMS concurrent sweep),回收所有的垃圾对象。
    CMS收集器运行示意图如下:
  2. CMS收集器的缺点
  • 对CPU资源非常敏感。并发收集虽然不会暂停用户线程,但因为占用一部分CPU资源,还是会导致应用程序变慢,总吞吐量降低,CMS的默认收集线程数量是=(CPU数量+3)/4。
  • 无法处理浮动垃圾,可能出现"Concurrent Mode Failure"失败。在并发清除时,用户线程新产生的垃圾,称为浮动垃圾, 这使得并发清除时需要预留一定的内存空间,不能像其他收集器在老年代几乎填满再进行收集,也可以认为CMS所需要的空间比其他垃圾收集器大。如果CMS预留内存空间无法满足程序需要,就会出现一次"Concurrent Mode Failure"失败,这时JVM启用后备预案:临时启用Serail Old收集器,而导致另一次Full GC的产生。
  • 产生大量内存碎片。由于CMS基于"标记-清除"算法,清除后不进行压缩操作,产生大量不连续的内存碎片会导致分配大内存对象时,无法找到足够的连续内存,从而需要提前触发另一次Full GC动作。由于空间不再连续,CMS需要使用可用"空闲列表"内存分配方式,这比简单实用"碰撞指针"分配内存消耗大。

G1收集器

  1. 特点
  • 并行与并发,能充分利用多CPU、多核环境下的硬件优势,可以并行来缩短"Stop The World"停顿时间,也可以并发让垃圾收集与用户程序同时进行。
  • 分代收集,收集范围包括新生代和老年代 ,能独立管理整个GC堆(新生代和老年代),而不需要与其他收集器搭配,能够采用不同方式处理不同时期的对象。将整个堆划分为多个大小相等的独立区域(Region),新生代和老年代不再是物理隔离,它们都是一部分Region(不需要连续)的集合。
  • 结合多种垃圾收集算法,空间整合,不产生碎片,从整体看,是基于标记-整理算法,从局部(两个Region间)看,是基于复制算法,都不会产生内存碎片,有利于长时间运行。
  • 可预测的停顿:低停顿的同时实现高吞吐量,G1除了追求低停顿外,还能建立可预测的停顿时间模型,可以明确指定M毫秒时间片内,垃圾收集消耗的时间不超过N毫秒。
  1. 应用场景
  • 面向服务端应用,针对具有大内存、多处理器的机器
  • 最主要的应用是为需要低GC延迟,并具有大堆的应用程序提供解决方案。
  1. 设置参数
  • “-XX:+UseG1GC”:指定使用G1收集器
  • “-XX:InitiatingHeapOccupancyPercent”:当整个Java堆的占用率达到参数值时,开始并发标记阶段;默认为45
  • “-XX:MaxGCPauseMillis”:为G1设置暂停时间目标,默认值为200毫秒
  • “-XX:G1HeapRegionSize”:设置每个Region大小,范围1MB到32MB;目标是在最小Java堆时可以拥有约2048个Region
  1. 为什么G1收集器可以实现可预测的停顿?
    G1可以建立可预测的停顿时间模型,是因为:可以有计划地避免在Java堆的进行全区域的垃圾收集,G1跟踪各个Region获得其收集价值大小,在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region,这就保证了在有限的时间内可以获取尽可能高的收集效率。
  2. 一个对象被不同区域引用的问题
    一个Region不可能是孤立的,一个Region中的对象可能被其他任意Region中对象引用,判断对象存活时,是否需要扫描整个Java堆才能保证准确?在其他的分代收集器,也存在这样的问题。
    解决方法:
    无论G1还是其他分代收集器,JVM都是使用Remembered Set来避免全局扫描。每个Region都有一个对应的Remembered Set,每次Reference类型数据写操作时,都会产生一个Write Barrier暂时中断操作,然后检查将要写入的引用指向的对象是否和该Reference类型数据在不同的Region (其他收集器:检查老年代对象是否引用了新生代对象)如果不同,通过CardTable(CardTable是Remembered Set的一种实现)把相关引用信息记录到引用指向对象的所在Region对应的Remembered Set中。当进行垃圾收集时,在GC根节点的枚举范围加入Remembered Set,就可以保证不进行全局扫描,也不会有遗漏。
  3. G1收集器运作过程
    (1)初始标记(Initial Marking)。仅标记一下GC Roots能直接关联到的对象,且修改TAMS(Next Top at Mark Start),让下一阶段并发运行时,用户程序能在正确可用的Region中创建新对象,需要"Stop The World",但速度很快。
    (2)并发标记(Concurrent Marking)。 进行GC Roots Tracing的过程,刚才产生的集合中标记出存活对象,耗时较长,但应用程序也在运行,并不能保证可以标记出所有的存活对象。
    (3)最终标记(Final Marking)。为了修正并发标记期间因用户程序继续运作而导致标记变动的那一部分对象的标记记录,上一阶段对象的变化记录在线程的Remembered Set Log,这里把Remembered Set Log合并到Remembered Set中。需要"Stop The World",且停顿时间比初始标记稍长,但远比并发标记短,采用多线程并行执行来提升效率。
    (4)筛选回收(Live Data Counting and Evacuation)。首先排序各个Region的回收价值和成本,然后根据用户期望的GC停顿时间来制定回收计划,最后按计划回收一些价值高的Region中垃圾对象。回收时采用"复制"算法,从一个或多个Region复制存活对象到堆上的另一个空的Region,并且在此过程中压缩和释放内存,可以并发进行,降低停顿时间,并增加吞吐量。
    G1收集器运行示意图如下:

[1]https://www.cnblogs.com/cxxjohnson/p/8625713.html

发布了8 篇原创文章 · 获赞 1 · 访问量 45

猜你喜欢

转载自blog.csdn.net/qq_33898680/article/details/103962990