JVM篇——G1、ZGC以及Shenandoah垃圾回收器详细介绍和底层算法展开分析,一篇文档,拿下常见经典的垃圾回收器!!!!!!

发布时间:2024年01月24日

G1垃圾回收器

一、认识G1

G1(Garbage-First)是一款面向服务端应用的垃圾收集器,主要针对配备多核CPU及大容量内存的机器。G1垃圾收集器的目标是满足GC停顿时间的同时,还兼具高吞吐量的性能特征。它主要特点如下:
并行与并发:G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。此时用户线程可能会短暂停顿(STW)。G1拥有与应用程序交替执行的能力,部分工作可以和应用程序同时执行,因此,一般来说,不会在整个回收阶段发生完全阻塞应用程序的情况。
分代收集:从分代上看,G1依然属于分代型垃圾回收器,它会区分年轻代和老年代,年轻代依然有Eden区和Survivor区。但从堆的结构上看,它不要求整个Eden区、年轻代或者老年代都是连续的,也不再坚持固定大小和固定数量。
区域分代化(分区算法,部分回收):将堆内存分为一个一个的region,每个region可以是物理上不连续的空间。G1对region进行追踪,衡量每个region回收后的价值和回收所需时间(其实就是region回收的效率,回收后能清除较多空间的region优先级更高)。
垃圾优先:由于G1对垃圾回收的效率更加敏感,因此称G1是垃圾优先。G1有计划地避免在整个Java堆中进行全区域的垃圾收集。G1跟踪各个Region里面的垃圾堆积的价值大小(回收所获得的空间大小以及回收所需时间的经验值),在后台维护一个优先列表,每次根据允许的收集时间,优先回收价值最大的Region。

G1清理年轻代和老年代的算法:
这里需要注意:G1是物理上不分代,但是逻辑上分代的!!!
对于年轻代,G1采用复制算法。由于年轻代主要存储新创建的对象,其存活率较低,因此G1会将年轻代分为多个区域,每次只回收一部分区域,并将存活的对象复制到新的区域中,这样可以显著减少垃圾回收的停顿时间。
对于老年代,G1主要采用标记-整理算法。由于老年代存储了大量存活时间较长的对象,因此需要更复杂的算法来处理。G1会遍历所有对象,标记引用情况,清除对象后会对区域进行复制移动,以整合碎片空间。

二、G1核心思想

部分回收。
这里再介绍下Humongous:
在G1(Garbage-First)垃圾回收器中,Humongous对象是一个特殊的对象,其大小超过了某个阈值,通常是一个Region大小的50%。这种对象会被标记为Humongous,并且会被分配到老年代(Old Generation)中的一个特殊区域。如果一个Humongous对象无法被放入一个连续的H-Region中,G1会寻找一个连续的H-Region来存放该对象。如果找不到连续的H-Region,G1会启动Full GC来达到目的。
值得注意的是,如果老年代中有过多的Humongous对象,会大大增加Full GC的频率。因此,在程序中应尽量避免创建过大的对象,以减少Full GC的频率,提高程序的性能。

三、G1垃圾回收器优缺点

G1垃圾回收器的优点主要包括:
(1)并行与并发:G1在回收期间,可以有多个GC线程同时工作,有效利用多核计算能力。此时用户线程可以STW。
(2)分代收集:G1依然属于分代型垃圾回收器,它会区分年轻代和老年代。从分代上看,G1同时兼顾年轻代和老年代。对比其他回收器,或者工作在年轻代,或者工作在老年代。
(3)空间整合:G1将内存划分为一个个的region。内存的回收是以region作为基本单位的。Region之间是复制算法,但整体上实际可看作是标记-压缩(Mark-Compact)算法,两种算法都可以避免内存碎片。这种特性有利于程序长时间运行,分配大对象时不会因为无法找到连续内存空间而提前触发下一次GC。尤其是当Java堆非常大的时候,G1的优势更加明显。
(4)可预测的停顿时间模型:这是G1相对于CMS的另一大优势,G1除了追求低停顿外,还能建立可预测的停顿时间模型,能让使用者明确指定在一个长度为M毫秒的时间片段内,消耗在垃圾收集上的时间不得超过N毫秒。
(5)压缩空间:G1可以避免产生内存碎片。
(6)简单配置就能达到很好的性能。
然而,G1垃圾回收器也存在一些缺点:
(1)记忆集RSet会占用比较大的内存,因此不建议在小内存下使用G1,推荐至少6G。
(2)对CPU的负载可能会更大一点。
(3)由于采用复制算法,GC垃圾回收过程对象复制转移会占用较多的内存,更容易出现回收失败(Allocation (Evacuation) Failure)的问题。
(4)可能会降低吞吐量。虽然G1收集器的垃圾收集暂停时间通常要短得多,但应用程序吞吐量也往往略低一些。相当于把一次垃圾回收的工作,分开多次进行执行(主要指老年代),单次暂停的时间虽然更加可控,但是由于每次垃圾回收的空间会更少,总体来说垃圾回收的效率会更低,暂停的总时间会更长,所以吞吐量往往会略低一些。
(5)如果停顿时间过短的话,可能导致每次选出的回收集只占堆内存很小一部分,收集器收集的速度逐渐跟不上分配器的分配速度,进而导致垃圾慢慢堆积,最终造成堆空间占满,引发Full GC反而降低性能。

ZGC垃圾回收器

一、ZGC概念
ZGC是Golang的垃圾回收器
ZGC(Z Garbage Collector)是一种可扩展的低延迟垃圾收集器,主要被设计用于处理超大内存(TB级别)的垃圾回收。其核心是一个并发垃圾回收器,具有以下特性:
1、停顿时间不超过10ms。
2、停顿时间不会随着堆的大小或者活跃对象的大小而增加。
3、支持堆范围为8MB~16TB。?
ZGC在JDK 11中首次以实验性的特性引入,并在JDK 15中正式投入生产使用。其设计目标是提供极致的低延迟,并且官方明确指出JDK 15中的ZGC不再是实验性质的垃圾收集器,建议投入生产使用。在JDK 16发布后,GC的暂停时间已经缩小到1ms以内,并且时间复杂度是O(1),这意味着GC的停顿时间是一个固定值,并不会受堆内存大小影响。

二、ZGC核心算法
ZGC的核心算法是并发处理算法,它利用全局空间视图的切换和对象地址视图的切换,结合STAB算法实现了高效的并发。染色指针和读屏障在ZGC中起到辅助作用,但并非其核心。

1、ZGC的并发处理算法主要通过以下方式实现:
不像G1那样再区分年轻代和老年代了,默认每100ms进行一次GC
全局空间视图的切换:ZGC初始化之后,整个内存空间的地址视图被设置为Remapped。在进入标记阶段时,视图会转变为Marked0或者Marked1。在标记阶段结束后,从标记阶段进入转移阶段时,视图再次设置为Remapped。
对象地址视图的切换:ZGC通过视图的切换和SATB(Silent Abort)算法实现并发处理。在对象的访问可能来自标记线程和应用程序线程时,ZGC会检查对象的地址视图。如果发现对象的地址视图是M0,说明对象是在进入标记阶段之后新分配的对象或者对象已经完成了标记(对象活跃),无须处理。如果发现对象的地址视图是Remapped,说明对象是前一阶段分配的,而且通过根集合可达,所以把对象的地址视图从Remapped调整为M0。
ZGC的并发处理算法利用全局空间视图的切换和对象地址视图的切换,结合SATB算法实现了高效的并发。这种算法可以在应用程序正常运行时进行垃圾回收,从而减少应用程序的停顿时间。

2、染色指针
ZGC的染色指针是一种直接将少量额外的信息存储在指针上的技术。具体来说,ZGC将其高4位提取出来存储4个标志信息,通过这些标志位,虚拟机可以直接从指针中看到其引用对象的三色标记状态(Marked0/Marked1)、是否进入了重分配集(即被移动过,Remapped)、是否只能通过finalize()方法才能访问到。由于这些标记位进一步压缩了原本就只有46位的地址空间,也直接导致ZGC能够管理的内存不可以超过4TB(2的42次方),而通过jdk13的扩展,现在ZGC可以管理的内存空间范围为(8MB-16TB,2的44次方)。
此外,染色指针技术使得ZGC在垃圾收集过程中能够大幅减少内存屏障的使用数量,设置内存屏障的目的是为了记录对象引用的变动情况。如果将这些信息直接维护在指针中,显然可以省略一些专门的记录操作。而实际上ZGC并没有使用写屏障,因为它不设分代,天然没有跨代引用的问题。
染色指针技术还使得ZGC能够改善GC过程的STW(Stop-The-World)时间。具体来说,Page内的存活对象被移走之后马上就能被释放和重用,不必等到整个堆上对该Page的引用都被修正之后才能释放和清理。这得益于染色指针的自愈(Self-Healing)特性。
总的来说,ZGC的染色指针技术是一种高效的内存管理技术,能够提高垃圾回收的效率和响应速度,减少内存屏障的使用数量,改善GC过程的STW时间。

3、读屏障
ZGC中的读屏障是一种JVM向应用代码中插入一小段代码的技术。当应用线程从堆中读取对象的引用时,会先执行这段代码。
具体来说,读屏障在解释执行时通过load相关的字节码指令加载数据。作用是在对象标记和转移过程中,判断对象的引用地址是否满足条件,并作出相应动作。读屏障会对应用程序的性能有一定影响,据测试,最多百分之4的性能损耗。但这是ZGC并发转移的基础,为了降低STW,设计者认为这点牺牲是可接受的。
在ZGC中,读屏障技术用于解决并发转移时出现指针无效的问题。当应用线程读取一个未完成转移的对象时,由于对象尚未完成转移,其指针可能无效。此时,读屏障技术确保在对象转移完成之前不会读取该对象,从而避免了指针无效的问题。
通过读屏障技术,ZGC可以在并发转移过程中实现高效的内存管理,降低STW时间,提高程序的响应速度和吞吐量。同时,由于读屏障技术的引入,ZGC也能够在不影响应用程序性能的情况下实现垃圾回收的自动管理。

Shenandoah垃圾回收器

一、Shenandoah概念
Shenandoah是Red Hat公司开发的垃圾回收器,其设计目标是降低垃圾回收停顿时间,将停顿时间控制在10ms以内。Shenandoah基于Region的堆内存布局进行垃圾回收,具有以下特点:
支持并发的整理回收算法。
默认不使用分代集。
不使用记忆集,改为连接矩阵。
Shenandoah的工作过程大致分为九个阶段:初始标记、并发标记、最终标记、并发清理、并发回收等。其中,并发回收阶段是Shenandoah与之前HotSpot中其他收集器的核心差异。在这个阶段,Shenandoah要把回收集里面的存活对象先复制一份到其他未被使用的Region之中。
与G1垃圾回收器相比,Shenandoah在代码实现上使用了很多G1的代码,因此有很多特点和G1相同。然而,Shenandoah在内存回收上面和G1有很多不同,例如在并发标记和并发回收方面有自己独特的设计和实现。
二、Shenandoah核心算法

Shenandoah的核心算法是并发标记和整理回收算法
Shenandoah采用并发标记算法,通过在标记阶段引入并发处理,使得垃圾回收器可以在应用程序运行时进行标记操作,从而减少应用程序的停顿时间。在并发标记阶段,Shenandoah使用读屏障技术来跟踪和记录内存中的写操作,以便更准确地标记活动的对象。
在整理回收阶段,Shenandoah采用并发整理算法,将存活的对象复制到未被使用的内存区域中,从而实现内存的回收和重利用。在并发整理阶段,Shenandoah通过使用染色指针技术来减少内存屏障的使用数量,提高垃圾回收的效率。染色指针技术是一种将少量额外信息存储在指针中的技术,可以加速对象引用状态的获取和修改速度,减少对内存屏障的依赖。

至此关于G1、ZGC以及Shenandoah垃圾回收器介绍完毕,里面涉及一些算法,本文介绍的不是很详细,后续还会深入的补充下相关算法的详细介绍,当然也希望大家可以去主动了解下涉及到的算法,还是很有意思的!

文章来源:https://blog.csdn.net/CNpeaceful/article/details/135776374
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。