前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JVM 系列(5) —— 垃圾收集器

JVM 系列(5) —— 垃圾收集器

作者头像
求和小熊猫
发布2020-12-29 11:18:05
2790
发布2020-12-29 11:18:05
举报

经典垃圾收集器

目前衡量垃圾收集器的三个最重要指标分别为:

  • 内存占用 (Footprint)
  • 吞吐量 (Throughtput)
  • 延迟 (Latency)

这三个指标也被称为不可能的三角,即无法做到三者间的,随着硬件性能的提升,人们反而可以容忍内存占用的扩大,对延迟的容忍度反而降低。

在这里插入图片描述
在这里插入图片描述

该图展示了七种用于不同分代的分代收集器

Serial/Serial Old 收集器

Serial 收集器是一个新生代收集器,采用复制算法。Serial Old 收集器是 Serial 收集器的老年代版本,采用标记整理算法。

Serial 收集器与 Serial Old 收集器都是一个单线程收集器,在垃圾回收时,他们会暂停所有用户线程,直到垃圾收集的结束。一旦所需要回收的垃圾较多时,则用户线程等待的时间便会相当漫长, 从而影响用户体验。

Serial 收集器是 HotSpot 虚拟机运行在客户端下默认的新生代收集器,与其他收集器相比简单高效,并且内存消耗较少, 在单核或多核环境下,几乎没有线程切换的开销。

Serial Old 收集器主要也是供客户端模式下的 HotSpot 虚拟机使用。Serial Old 还可以在服务端作为 CMS 收集器发生 Concurrent Mode Failure 失败后的备份预案。

在这里插入图片描述
在这里插入图片描述
ParNew 收集器

ParNew 收集器可以视为是 Serial 收集器的多线程版本。因为其所有控制参数、收集算法、Stop The World、对象分配规则、回收策略都与Serial 收集器完全一致。

ParNew 收集器在单核环境中的性能始终不如 Serial 收集器。

Parallel Scavenge/ Parallel Old 收集器

Parallel Scavenge 收集器也是一个新生代收集器,也是基于标记复制算法实现的垃圾收集器,也是能够并行收集的多线程收集器。如果 CMS 收集器关注点在与尽量缩短垃圾收集时的时间,Parallel Scavenge 收集器则的目标则是达到一个可控的吞吐量。

在这里插入图片描述
在这里插入图片描述

Parallel Old 收集器可以视为 Parallel Scavenge 收集器的老年代版本

在这里插入图片描述
在这里插入图片描述
CMS 收集器

CMS (Concurrent Mark Sweep) 收集器是一种以获取最短回收停顿时间为目标的收集器,通常用于 B/S 系统服务端上,对服务的响应速度较为关注。

CMS 收集器工作分为以下四个步骤

  1. 初始标记 (CMS inital Mark) : 标记一下 GC Roots 能直接关联到的对象,该步骤需要 Stop The Wrold
  2. 并发标记 (CMS concurrent mark) : 对 GC Roots 进行 Tracing
  3. 重新标记 (CMS remark) : 修正因用户程序继续运作而导致标记产生变动的那一部分对象的标记记录,需要 Stop The World
  4. 并发清楚 (CMS concurrent sweep) : 回收垃圾
在这里插入图片描述
在这里插入图片描述

concurrent mode failure 由于 CMS是和用户线程并行的,如果在并行清理的过程中,老年代产生的垃圾不足以容纳应用产生的垃圾则会产生 concurrent mode failure

CMS 有三个明显的缺点

  • 对CPU 资源非常敏感。在并发标记过程中,虽然不会对用户进程产生影响,但是由于占用了 CPU 资源,导致总吞吐量下降,用户进程便会变慢,CMS 默认启动线程数量是 (CPU 数量 + 3),这就意味着当 CPU 数量不足 4 个时,CMS 便会对用户线程产生很大的影响。
  • CMS 无法处理浮动式垃圾。由于 CMS 在并发标记和并发清理阶段还是会有用户线程产生垃圾,但是这部分垃圾产生在标记结束之后,因此 CMS 只好在下一次垃圾回收时清理掉这些垃圾。
  • CMS 是一款基于 标记清楚算法实现的收集器,这便意味着有大量的空间碎片产生。

CMS 在解决“对象消失”问题时采用的是增量更新的方法

Garbage First (G1) 收集器

G1 是一款主要面向服务端的垃圾收集器,G1 相较于其他几个收集器,可以面向堆内任何部分来组成回收集,而不只是单单面对新生代(Minor GC),老年代(Major GC)或者整堆(Full GC)这三者中的一个。

G1 回收器的回收标准并不是判断对象是哪个代,而是那块内存中存放的垃圾数量最多,垃圾回收的收益就最大。

G1 开创了基于 Region 的堆内存布局。G1 不再坚持以固定的大小以及固定的数量来分代划分区域,而是把连续的 Java 堆划分成为多个大小不等的独立区域。每个区域可以根据需要来扮演 Eden、Survivor、或者老年代空间,这样无论是新创建的对象还是已存活了一段时间的对象还是 熬过多次收集的对象,都能取到很好的收集效果。

在 Region 中还有一个名为 Humongous 区域,专门用来存储大对象,只要是大小超过 Region 一半的对象就会被认定为大对象。

G1 收集器虽然仍然保留了新生代和老年代的概念,但是 新生代和老年代都不是固定的了,他们之间的内存区域并不是相连的,因此避免了在整个 Java 堆中进行全区域的垃圾收集。

G1 收集器也需要耗费相当于 Java堆中 10% ~ 20%的额外内存来维持收集器的工作。

G1 收集器在解决 “对象消失”问题时采用的是原始快照 (SATB) 算法。

G1 垃圾收集器为了解决在垃圾回收过程中新对象内存的分配问题时,G1 为每一个 Region 都设置了两个 TAMS (Top at Mark Start) 的指针。用于在 Region 上划分一部分空间用作新对象内存的分配,并发回收时新分配的对象地址都必须要在这两个,G1 回收器在垃圾回收时会默认这些对象时存活的。

G1 回收器回收的四个工作阶段:

  • 初始标记 (Intial Marking): 标记 GC Roots 能直接关联到的对象,并且修改 TAMS 的值,让下一阶段用户线程并发运行时能正确的在 Region 中正确分配新对象。
  • 并发标记 (Concurrent Marking): 从 GC Roots 开始进行可达性分析
  • 最终标记 (Final Marking):对用户线程做一个短暂的停留,用于处理并发结束后少量的 STAB 记录
  • 筛选回收 (Live Data Counting and Evacuation):负责更新 Region 的统计数据,对各个Region 的回收价值和成本进行排序,根据用户的期望停顿时间,来制定回收计划。
在这里插入图片描述
在这里插入图片描述

低延时垃圾回收器

Shenandoah 垃圾收集器

Shenandoah 和 G1 一样采用基于 Region 的内存布局,同样有着存放大对象的 Humongous Region ,默认回收策略也是优先处理回收价值最大的 Region。

Shenandoah 与 G1 收集器最大的区别在与堆内存的管理上有三个明显的区别:

  • 支持并发整理算法:G1 能支持与用户线程并行,却不能支持用户线程并发。
  • Shenandoah 默认不使用分代收集
  • Shenandoah 摒弃了在 G1 中耗费的大量内存和计算资源去维护的记忆集。而改用连接矩阵,这样降低了处理跨代指针时的记忆集的维护和消耗,也减少伪共享问题的发生。如下图,就是记录了一个由 Region 2 指向 Region 1 的指针和 Region 1 指向 Region 4 的指针。
在这里插入图片描述
在这里插入图片描述

Shenandoah 收集器的工作过程可以分为以下 9 个阶段:

  • 初始标记 (Initial Marking): “Stop the World”,标记与 GC Roots 直接关联的对象
  • 并发标记 (Concurrent Marking): 遍历对象图,标记全部可达对象,该过程用户线程并发运行。
  • 最终标记 (Final Marking): 处理剩余 SATB 扫描,并统计出回收价值最高的 Region,将这些 Region 组成一个回收集。
  • 并发清理 (Concurrent Cleanup):用于清理整个 Region 连一个存活对象都无法找到的 Region
  • 并发回收 (Concurrent Evacuation):该阶段 Shenandoah 要把回收集中的存活对象先复制到其他 Region 中去。由于该过程是和用户线程并发执行的,用户线程会对要移动的对象进行不停的读写访问,为了解对象的引用地址发生改变的问题,Shenandoah 通过读写屏障和转发指针来解决这一问题。
  • 初始引用更新 (Initial Update Reference):建立一个线程集合,确保所有并发回收阶段中进行的收集器线程都已近完成了对象移动的任务
  • 并发引用更新 (Concurrent Update Reference): 进行引用更新操作,将堆中所有旧对象的地址修正到复制后的新地址,该过程只需要按照内存的物理地址的顺序,线性的搜索出引用类型,将旧值改为新值即可。
  • 最终引用跟新 (Final Update Reference): 解决了堆中的引用更新后,还需要修正存在于 GC Roots 中的引用。该过程会进行 “Stop The World”
  • 并发清理 (Concurrent Cleanup): 回收 Region 的内存空间

读写屏障和 “ Brooks Pointers” 在原有对象的前面加上一个新引用字段,在正常不处于并发移动的情况下,该引用对象指向自己。在对象移动完成后,指针则会直接指向新的地址上。这个指针便是 "Brooks Pointers"。

由于复制过程是和用户线程并发运行的,如果在运行过程中有用户线程想要对移动中的对象进行写操作,势必会造成一些问题。为了避免这种情况的发生,可以对指针的访问采取同步错失。即用户线程和收集器线程只能有其中一个对其进行访问。或者也可以采用 CAS 操作,而 Shenandoah 垃圾收集器采用的也是 CAS 操作。

如果在复制过程中进行单纯的读操作,则没有必要担心。因为无论指针指向哪一个,都不会影响读取的结果。

ZGC 收集器

ZGC 是一款基于 Region 内存布局的,不设分代的,使用了读屏障、染色指针和内存多重映射技术来实现可并发的标记整理算法的低延时垃圾收集器。

ZGC 的 Region 也称为 Page 或 ZPage。

Page 的堆内存区域可以分为三种:

  • 小型 Region:2M 容量,用于存放小于 256kb 的小对象
  • 中性 Region:32M 容量,用于存放大于等于 256kb 到小于 2 mb 之间的对象
  • 大型 Region:容量不固定,单必须是 2MB 的整数倍,用于存放 4MB 或以上的大对象。

染色指针是一种直接将少量额外信息存储在指针上的技术。在 64 位系统中,理论可以访问的内存高达 2 64 2^{64} 264 个字节。但在实际上,由于硬件和操作系统的限制,可用于寻址的范围有限。

在 Linux 系统下 64 位指针的高 18 位不能用于寻址。但剩余 46 位指针所能支持的64TB 依旧能满足大部分服务器的需求。因此染色指针将 46 位指正的前4位取出来,用于存储 4 个标志位。

染色指针的优点:

  • 染色指针的使用可以使得某个 Region 内的存活对象被移走之后,Region 就可以立刻被回收使用
  • 染色指针的使用减少了内存屏障的使用量

PS: 染色指正不支持 32 位系统

ZGC 的运作过程

  • 并发标记 (Concurrent Mark): 此过程类似于 G1、Shenandoah 一样,用于遍历对象图,做可达性分析。但是由于采用了染色指正技术,因此对于对象的标记无需在对象上进行修改,只需要修改染色指针相应的标记位。
  • 并发预备从分配 (Concurrent Prepare for Relocate): 依据特殊条件计算出要处理那些 Region,并将这些 Region 组成重分配集。
  • 并发重分配 (Concurrent Relocate): 改过成将重分配集上还活着的对象复制到新的 Region 上去。
    • 并发重映射 (Concurrent Remap): 修正整个堆中指向重分配集旧对象的所有引用

小总结

绿色为 Stop the World 的时间,红色为可并发运行的过程。由图,大家可以对 GC 的过程有一个直观的感受

在这里插入图片描述
在这里插入图片描述
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2020-12-27 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 经典垃圾收集器
    • Serial/Serial Old 收集器
      • ParNew 收集器
        • Parallel Scavenge/ Parallel Old 收集器
          • CMS 收集器
            • Garbage First (G1) 收集器
            • 低延时垃圾回收器
              • Shenandoah 垃圾收集器
                • ZGC 收集器
                • 小总结
                领券
                问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档