GVM垃圾收集器

Serial收集器(新生代)

        Serial(串行)收集器是最基本、历史最悠久的垃圾收集器,采用“标记-复制”算法负责新生代的垃圾收集。它是Hotspot虚拟机运行在客户端模式下的默认新生代收集器。

        它是一个单线程收集器。它会使用一条垃圾收集线程去完成垃圾收集工作,并且它在进行垃圾收集工作的时候,必须暂停其他所有的工作线程(“Stop The World”),知道收集结束。

        这样的设计带来的好处就是:简单高效。对于内存资源受限的环境,它是所有收集器中额外内存消耗最小的收集器。适合单核处理器或处理器核心数较少的环境,每次收集几十MB甚至一两百MB的新生代内存,垃圾收集的停顿时间完全可以控制在十几毫秒或几十毫秒,最多一百多毫秒。

Serial Old收集器(老年代)

        Serial Old收集器同样是一个单线程收集器,采用“标记-整理”算法负责老年代的垃圾收集,主要用于客户端模式下的HotSpot虚拟机使用。

        如果在服务器端使用,它主要有两种用途:

  1. 在JDK5及以前版本,与Parallel Scavenge收集器搭配使用
  2. 作为CMS收集器发生失败时的后备预案

GVM垃圾收集器_第1张图片

ParNew收集器(新生代)

        ParNew收集器是一个多线程的垃圾收集器。它是运行在Server模式下的虚拟机首要选择,可以与Serial Old,CMS垃圾收集器一起搭配工作,采用“标记-复制”算法。

GVM垃圾收集器_第2张图片

Parallel Scavenge收集器(新生代)

        Parallel Scavenge收集器也是一款新生代收集器,使用“标记-复制”算法实现的多线程收集器

        Parallel Scavenge收集器与其他收集器的目标不同,CMS等其他收集器目标是尽可能缩短垃圾收集时用户线程的停顿时间。但是 Parallel Scavenge 收集器的目标则是达到一个可控制的吞吐量。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值。

         如果虚拟机完成某个任务,用户代码加上垃圾收集总共耗费了 199 分钟,其中垃圾收集花掉 1 分钟,那吞吐量就是 99% 。停顿时间越短,就越适合需要与用户交互或需要保证服务响应质量的程序,良好的响应速度能提升用户体验:而高吞吐量则可以最高效率地利用处理器资源,尽快完成程序的运算任务,主要适合在后台运算而不需要太多交互的分析任务。

Parallel Old收集器(老年代)        

        Parallel old 收集器是一个多线程的垃圾收集器,使用“标记-整理”算法是 Parallel scavenge 收集器的老年代版本。

        在注重吞吐量或者处理器资源较为稀缺的应用场景,都可以优先考虑Parallel scavenge 收集器 + Parallel old 收集器这个收集器组合。

        这个收集器是直到 JDK6 时才开始提供的,在此之前,新生代的 Parallel scavenge 收集器一直处于相当尴尬的状态,原因是如果新生代选择了 Parallel scavenge收集器,老年代除了 serial old 收集器以外别无选择,其他表现良好的老年代收集器,如 CMS 无法与它配合工作。由于老年代 serial old 收集器在服务端应用性能上的“拖累”,使用 Parallel scavenge 收集器也未必能在整体上获得吞叶量最大化的效果。同样,由于单线程的老年代收集中无法充分利用服务器多处理器的并行处理能力在老年代内存空间很大而且硬件规格比较高级的运行环境中,这种组合的总吞叶量甚至不一定比 ParNew 加 CMs 的组合来得优秀。

GVM垃圾收集器_第3张图片

CMS收集器(老年代)

        CMS ( concurrent Mark Sweep ) 收集器是一种以获取最短回收停顿时间为目标的收集器,基于“标记-清除”算法实现,是 HotSpot 虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程 (基本上) 同时工作。

        目前很大一部分的 java 应用集中在互联网站点或者基于浏览器的 B/s 架构的服务器务端上,这类应用通常都会较为关注服务的响应速度,希望系统停顿时间尽可能短,以给用户带来自好的交互体验。所以, CMS 收集器非常符合这类应用的收集场景。

工作流程:

整个过程包括四个步骤:
1.初始标记 (CMS initial mark) : 标记 下 GC Roots能直接关联到的对象,速度很快;
2.并发标记 (CMS concurrent mark): 从 C Roots 的直接关联对象开始遍历整个对象图的过程,这个过程耗时较长,但是不需要停顿用户线程,可以与垃圾收集线程一起并发运行;
3.重新标记 (CMS remark): 重新标记阶段,是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录,这个阶段的停顿时间一般会比初始标记阶段的时间长,远远比并发标记阶段时间短;
4.并发清除 (CMS concurrent sweep) : 清理删除掉标记阶段判断的已经死亡的对象,由于不需要移动存活对象,所以这个阶段也是可以与用户线程同时并发的

GVM垃圾收集器_第4张图片

优点和缺点

主要优点: 并发收集、低停顿
主要缺点:

  • 影响用户线程的执行效率:并发标记并发清除时,是和用户线程一起运行的,收集过程中肯定占用了用户程序的 CPU 资源。 CMS 默认启动的回收线程数是 (CPU数量+3)/4 ,当 CPU 数量在 4 个以上时,垃圾回收线程占用不少于 25% 的 CPU资源,势必影响用户线程的执,行效率。
  • 无法处理浮动垃圾:在并发清除阶段,用户线程并没有停止,所以还会继续产生新的垃圾,只能等待下一次收集时才能进行回收,这部分垃圾被称为“浮动垃圾”。
  • 产生大量空间碎片: 因为CMS收集器是基于“标记-清除”算法实现的,所以在进行大量的垃圾回收时,会产生很多不连续的内存空间。这是使用“标记-清除”算法都会有的缺点。 

        由于垃圾收集阶段用户线程还需要持续运行,所以需要预留足够的内存空间提供给用户线程使用,因此 CMS 收集器不能像其它收集器那样等到老年代几乎完全被填满了再进行收集。

  • 在 JDK6 的默认设置中, CMS 收集器的启动值为 92% ,代表老年代使用了 92%的空间后,就会启动 CMS 收集器
  • 如果 CMS 运行期间,无法满足程序分配新对象的需要,就会出现一次“并发失败”,这时候虚拟机将临时启动 serial old 收集器进行老年代的垃圾收集。

G1收集器(老年代)

什么是G1 垃圾收集器

        G1 ( Garbage-First )是一款面向服务器的垃圾收集器,主要针对配备多颗处理器、大容量内存的机器。它不再严格按照分代思想进行垃圾回收。 G1 采用局部性收集的设计思路和基于 Region 的内存布局形式。

G1 垃圾收集器的结构

        G1 采用局部性收集的思想,对于堆空间的划分,采用 Region 为单位的内存划分方式:

        G1 垃圾回收器把堆划分成 2048 个大小相同的独立区域 ( Region ) ,每个 Region 的大小取值范围是 1MB-32MB,且应为 2的N 次幕,即 1MB,2MB ,4MB8MB ,16MB,32MB。

        每个 Region 都会代表某一种角色,H 、s 、E、0。E 代表 Eden 区,s 代表 Survivor 区, H 代表的是 Humongous  G1 用来分配大对象的区域,对于 Humongous 也分配不下的超大对象,会分配在连续的 N 个 Humongous中),剩余的深蓝色代表的是 old 区,灰色的代表的是空闲的 region。

        这种思想上的转变和设计,使得G1可以面向堆内存任何部分来组成回收集来进行回收,衡量标准不再是它属于哪个分代,而是哪块内存存放的垃圾最多,回收收益最大,这就是G1收集器的 Mixed GC模式,即混合GC模式。

GVM垃圾收集器_第5张图片

G1 垃圾收集器工作流程

初始标记( Initial Marking ): 这个阶段仅仅只是标记GC Roots能直接关联到的对象,这阶段需要停顿线程,但是耗时很短.

并发标记( Concurrent Marking ): 从GC Roots开始对堆的对象进行可达性分析,递归扫描整个堆里的对象图,找出存活的对象,这阶段耗时较长,但是可以与用户程序并发执行。

最终标记( Final Marking ): 对用户线程做另一个短暂的暂停,用于处理并发阶段结束后遗留记录。

筛选回收( Live Data counting and Evacuation ): 负责更新 Region 的统计数据,对各个 Region 的回收价值和成本进行排序,根据用户所期望的停顿时间来制定回收计划。可以自由选择多个 Region 来构成会收集,然后把回收的那部分 Region 中的存活对象==>复制==>到空的Region中,最后对那些 Region 进行清空。

GVM垃圾收集器_第6张图片

G1 垃圾收集器的特点

        并行与并发: G1 能充分利用 CPU 、多核环境下的硬件优势,使用多个 CPU ( CPU 或者 CPU 核心) 来缩短 stop-The-world 停顿时间。部分其他收集器原本需要停顿 Java 线程执行的 GC 动作, G1 收集器仍然可以通过并发的方式让 java 程序继续执行。

        分代收集: 虽然G1 可以不需要其他收集器配合就能独立管理整个 GC 堆,但是还是保留了分代的概念。但它能够采用不同方式去处理新创建的对象和已存活一段时间、熬过多次 Gc 的日对象来获取更好的收集效果。

        空间整合: G1 从整体来看是基于“标记-整理”算法实现的收集器,从局部 (两个 Region 之间) 上来看是基于“标记-复制”算法实现的。这意味着 G1 运行期间不会产生内存空间碎片,收集后能提供规整的可用内存。此特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次 GC 。

        用户指定期望停顿: 允许用户指定期望的停顿时间是 G1 收集器很强大的一个功能,设置不同的期望停顿时间,可以让 G1 在不同的场景下取得吞吐量和延迟之间的最佳平衡。 G1 的默认停顿目标为 200 毫秒,一般来说,设置为一百毫秒至两百毫秒这个区间都很正常。如果期望停顿时间设置过短,会导致由于停顿目标时间太短,导致每次筛选出来的回收集只占堆内存很小的一部分,收集器的收集速度会跟不上分配速度,导致垃圾慢慢堆积。

G1 垃圾收集器与CMS垃圾收集器的区别

        算法不同: CMS 采用“标记-清除”容易产生内存碎片,执行若干次 GC 后进行 1 次碎片整理。 G1 从整体来看是基于“标记-整理”算法实现的收集器,从局部(两个 Region 之间) 上来看是基于“标记-复制”算法实现。意味着G1垃圾收集器不会产生内存空间碎片,垃圾收集完成后,能提供规整的可用内存,不会导致因为大对象分配内存时无法找到连续内存空间而提前触发垃圾收集。

        场景不同: 小内存应用上 CMS 的表现大概率优于 1 而在大内存应用中, G1 则能发挥优势。大小内存的参考值分水岭大概在 6GB-8GB

你可能感兴趣的:(java,jvm,算法)