JVM垃圾回收器整理

前言

7种经典的垃圾收集器:

  • 串行回收器:Serial、Serial Old
  • 并行回收器:ParNew、Parallel Scavenge、Parallel Old
  • 并发回收器:CMS、G1

垃圾分代之间的关系:

  • 新生代收集器:Serial、ParNew、Paralle Scavenge;
  • 老年代收集器:Serial old、CMS、Parallel old;
  • 整堆收集器:G1;

在这里插入图片描述

Serial

单线程收集器,新生代,采用复制算法

这个收集器是一个单线程的收集器,但它的“单线程”的意义并不仅仅说明它只会使用一个CPU或一条收集线程去完成垃圾收集工作,会进行SWT,停掉除了GC线程之外的所有的工作线程,直到它收集结束,才继续执行其它的工作线程。

在这里插入图片描述

-XX:+UseSerialGC -XX:+UseSerialOldGC

Serial Old

单线程收集器,老年代,采用标记整理算法

Serial Old是Serial收集器的老年代版本,它同样是一个单线程收集器。会进行SWT,停掉除了GC线程之外的所有的工作线程,直到它收集结束,才继续执行其它的工作线程。

它主要有两大用途:一种用途是在JDK1.5以及以前的版本中与Parallel Scavenge收集器搭配使用,另一种用途是作为CMS收集器的后备方案。

优势:

  • 简单而高效(与其他收集器的单线程比),对于限定单个 CPU 的环境来说,Serial
    收集器由于没有线程交互的开销,专心做垃圾收集自然可以获得最高的单线程收集效率。
  • 运行在 Client 模式下的虚拟机是个不错的选择
  • 在用户的桌面应用场景中,可用内存一般不大(几十 MB 至一两百 MB),可以在较短时间内完成垃圾收集(几十 ms 至一百多 ms),只要不频繁发生,使用串行回收器是可以接受的。
-XX:+UseSerialGC -XX:+UseSerialOldGC

ParNew

多线程收集器,新生代,采用复制算法

如果说 Serial GC 是年轻代中的单线程垃圾收集器,那么 ParNew 收集器则是 Serial 收集器的多线程版本。Par 是 Parallel 的缩写,New:只能处理的是新生代。ParNew 收集器除了采用并行回收的方式执行内存回收外,与Serial 回收器,两款垃圾收集器之间几乎没有任何区别。

ParNew采用了多线程垃圾回收机制,ParNew垃圾回收器在执行Minor GC时,会将JVM中的工作线程全部停止掉,禁止程序在运行时创建新的Java对象,然后自己用多个垃圾回收线程去进行垃圾回收

ParNew收集器的垃圾回收过程包括标记、清理和整理三个阶段。在标记阶段,它会标记出所有存活的对象;在清理阶段,它会回收未被标记的对象;在整理阶段,它会重新整理内存空间,以便后续的垃圾回收操作。

ParNew收集器的优点:
ParNew收集器的优点包括支持多线程垃圾回收、能够与Parallel Scavenge收集器无缝配合、能够充分利用多核CPU的并行处理能力等。此外,它还可以通过调整参数来控制垃圾回收的频率和暂停时间等。

ParNew和Parallel Scavenge一样的,区别组了做了增强,以便能让它和CMS配合使用。

-XX:+UseParNewGC

Parallel Scavenge

多线程收集器,新生代、采用复制算法

Parallel收集器其实就是Serial收集器的多线程版本,除了使用多线程进行垃圾收集外,其余行为(控制参数、收集算法、回收策略等等)和Serial收集器类似。默认的收集线程数跟cpu核数相同,当然也可以用参数(-XX:ParallelGCThreads)指定收集线程数,但是一般不推荐修改。

Parallel Scavenge收集器关注点是吞吐量(高效率的利用CPU)。CMS等垃圾收集器的关注点更多的是用户线程的停顿时间(提高用户体验)。所谓吞吐量就是CPU中用于运行用户代码的时间与CPU总消耗时间的比值。 Parallel Scavenge收集器提供了很多参数供用户找到最合适的停顿时间或最大吞吐量,如果对于收集器运作不太了解的话,可以选择把内存管理优化交给虚拟机去完成也是一个不错的选择。

在这里插入图片描述

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

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

Parallel Old

多线程收集器,老年代,采用标记整理算法

Parallel Old是Parallel Scavenge收集器的老年代版本

-XX:+UseParallelGC -XX:+UseParallelOldGC

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

CMS

多线程收集器,老年代,采用标记—清除算法

CMS(Concurrent Mark Sweep)收集器是一种以获取最短回收停顿时间(吞吐量)为目标的收集器。它非常符合在注重用户体验的应用上使用,它是HotSpot虚拟机第一款真正意义上的并发收集器,它第一次实现了让垃圾收集线程与用户线程(基本上)同时工作。

从名字中的Mark Sweep这两个词可以看出,CMS收集器是一种 “标记-清除”算法实现的,它的运作过程相比于前面几种垃圾收集器来说更加复杂一些。整个过程分为四个步骤:

  • 初始标记(STW): 暂停所有的其他线程(STW),并记录下gc roots直接能引用的对象,速度很快。
  • 并发标记: 并发标记阶段就是从GC Roots的直接关联对象开始遍历整个对象图的过程, 这个过程耗时较长但是不需要停顿用户线程, 可以与垃圾收集线程一起并发运行。因为用户程序继续运行,可能会有导致已经标记过的对象状态发生改变。
  • 重新标记(STW): 重新标记阶段就是为了修正并发标记期间因为用户程序继续运行而导致标记产生变动的那一部分对象的标记记录(主要是处理漏标问题),这个阶段的停顿时间一般会比初始标记阶段的时间稍长,远远比并发标记阶段时间短。主要用到三色标记里的增量更新算法(见下面详解)做重新标记。
  • 并发清理: 开启用户线程,同时GC线程开始对未标记的区域做清扫。这个阶段如果有新增对象会被标记为黑色不做任何处理(见下面三色标记算法详解)。
  • 并发重置:重置本次GC过程中的标记数据。

在这里插入图片描述

优点:

  • 低停顿时间:CMS垃圾回收器通过并发标记和并发清除的方式,使得大部分的垃圾回收工作与应用程序的执行同时进行,从而减少了应用程序的停顿时间。

  • 垃圾回收和应用程序并发执行:CMS垃圾回收器在并发标记和并发清除阶段与应用程序并发执行,尽量减少了垃圾回收对应用程序性能的影响。

  • 老年代回收效率高:CMS垃圾回收器主要针对老年代进行回收,对于存活时间较长的对象能够高效地进行回收。

它是一款优秀的垃圾收集器,主要优点:并发收集、低停顿。但是它有下面几个明显的缺点:

  • 对CPU资源敏感(会和服务抢资源);
  • 无法处理浮动垃圾(在并发标记和并发清理阶段又产生垃圾,这种浮动垃圾只能等到下一次gc再清理了);
  • 它使用的回收算法-“标记-清除”算法会导致收集结束时会有大量空间碎片产生,当然通过参数-XX:+UseCMSCompactAtFullCollection可以让jvm在执行完标记清除后再做整理
  • 执行过程中的不确定性,会存在上一次垃圾回收还没执行完,然后垃圾回收又被触发的情况,特别是在并发标记和并发清理阶段会出现,一边回收,系统一边运行,也许没回收完就再次触发full gc,也就是"concurrent mode failure",此时会进入stop the world,用serial old垃圾收集器来回收

CMS 使用的是标记-清除的算法实现的,所以在 gc 的时候回产生大量的内存碎片,当剩余内存不能满足程序运行要求时,系统将会出现 Concurrent Mode Failure,临时 CMS 会采用 Serial Old 回收器进行垃圾清除,此时的性能将会被降低。

需要注意的是,CMS垃圾回收器在Java 9中已经被标记为过时(deprecated),并在Java 14中被移除。这是因为CMS垃圾回收器在处理大堆内存和长时间运行的应用程序时存在一些缺陷。建议在使用较新版本的JVM时,考虑使用更先进的垃圾回收器,如G1或ZGC。

-XX:+UseConcMarkSweepGC

G1

多线程收集器,全代,标记-整理

回收的时候会选择回收内存最多的一块region

G1 垃圾收集器的内存模型:

G1 收集器不采用传统的新生代和老年代物理隔离的布局方式,仅在逻辑上划分新生代和老年代,将整个堆内存划分为2048个大小相等的独立内存块Region,每个Region是逻辑连续的一段内存,具体大小根据堆的实际大小而定,整体被控制在 1M - 32M 之间,且为2的N次幂(1M、2M、4M、8M、16M和32M),并使用不同的Region来表示新生代和老年代,G1不再要求相同类型的 Region 在物理内存上相邻,而是通过Region的动态分配方式实现逻辑上的连续。

G1收集器通过跟踪Region中的垃圾堆积情况,每次根据设置的垃圾回收时间,回收优先级最高的区域,避免整个新生代或整个老年代的垃圾回收,使得stop the world的时间更短、更可控,同时在有限的时间内可以获得最高的回收效率。

通过区域划分和优先级区域回收机制,确保G1收集器可以在有限时间获得最高的垃圾收集效率。

分区Region:

G1 垃圾收集器将堆内存划分为若干个 Region,每个 Region 分区只能是一种角色,Eden区、S区、老年代区的其中一个,空白区域代表的是未分配的内存,最后还有个特殊的区域H(Humongous),专门用于存放巨型对象,如果一个对象的大小超过Region容量的50%以上,G1 就认为这是个巨型对象。在其他垃圾收集器中,这些巨型对象默认会被分配在老年代,但如果它是一个短期存活的巨型对象,放入老年代就会对垃圾收集器造成负面影响,触发老年代频繁GC。

为了解决这个问题,G1划分了一个H区专门存放巨型对象,如果一个H区装不下巨型对象,那么G1会寻找连续的H分区来存储,如果寻找不到连续的H区的话,就不得不启动 Full GC 了。

在这里插入图片描述

在这里插入图片描述

比较CMS和G1
算法实现:

  • CMS标记清除
  • G1标记整理(整体)、标记-复制(Region)

垃圾收集内存占用:G1由于对每个Region都要维护卡表,高于CMS;
并发可达性分析处理:

  • CMS:增量更新
  • G1:原始快照,STAB算法(两个TAMS)

写屏障

  • CMS:写后屏障维护卡表
  • G1:写前屏障跟踪并发指针变化,写后屏障维护卡表(队列、异步)

三色标记算法

在并发标记的过程中,因为标记期间应用线程还在继续跑,对象间的引用可能发生变化,多标和漏标的情况就有可能发生。漏标的问题主要引入了三色标记算法来解决。

三色标记算法是把Gc roots可达性分析遍历对象过程中遇到的对象, 按照“是否访问过”这个条件标记成以下三种颜色:

  • 黑色: 表示对象已经被垃圾收集器访问过, 且这个对象的所有引用都已经扫描过。 黑色的对象代表已经扫描过, 它是安全存活的, 如果有其他对象引用指向了黑色对象, 无须重新扫描一遍。 黑色对象不可能直接(不经过灰色对象) 指向某个白色对象。
  • 灰色: 表示对象已经被垃圾收集器访问过, 但这个对象上至少存在一个引用还没有被扫描过。
  • 白色: 表示对象尚未被垃圾收集器访问过。 显然在可达性分析刚刚开始的阶段, 所有的对象都是白色的, 若在分析结束的阶段, 仍然是白色的对象, 即代表不可达。

多标-浮动垃圾

在并发标记过程中,如果由于方法运行结束导致部分局部变量(gcroot)被销毁,这个gcroot引用的对象之前又被扫描过(被标记为非垃圾对象),那么本轮GC不会回收这部分内存。这部分本应该回收但是没有回收到的内存,被称之为“浮动垃圾”。浮动垃圾并不会影响垃圾回收的正确性,只是需要等到下一轮垃圾回收中才被清除。

另外,针对并发标记(还有并发清理)开始后产生的新对象,通常的做法是直接全部当成黑色,本轮不会进行清除。这部分对象期间可能也会变为垃圾,这也算是浮动垃圾的一部分。

漏标-读写屏障

漏标会导致被引用的对象被当成垃圾误删除,这是严重bug,必须解决,有两种解决方案: 增量更新(Incremental Update) 和原始快照(Snapshot At The Beginning,SATB) 。

  • 增量更新就是当黑色对象插入新的指向白色对象的引用关系时, 就将这个新插入的引用记录下来, 等并发扫描结束之后, 再将这些记录过的引用关系中的黑色对象为根, 重新扫描一次。 这可以简化理解为, 黑色对象一旦新插入了指向白色对象的引用之后, 它就变回灰色对象了。

  • 原始快照就是当灰色对象要删除指向白色对象的引用关系时, 就将这个要删除的引用记录下来, 在并发扫描结束之后, 再将这些记录过的引用关系中的灰色对象为根, 重新扫描一次,这样就能扫描到白色的对象,将白色对象直接标记为黑色(目的就是让这种对象在本轮gc清理中能存活下来,待下一轮gc的时候重新扫描,这个对象也有可能是浮动垃圾)

CMS 选择的是增量更新,G1选择的是原始快照

学习博客

垃圾回收器

垃圾回收相关知识点

JVM常见的垃圾回收器(详细)

  • 30
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值