当前位置: 首页 > 后端技术 > Java

ZGC的原理介绍

时间:2023-04-01 17:25:45 Java

参考http://cr.openjdk.java.net/~p...https://tech.meituan.com/2020...ZGCZGC的全称是Z垃圾收集器。是甲骨文公司开发的基于JDK11的垃圾收集器,2018年提交给OpenJDK。吞吐量(吞吐量与暂停时间成反比)尽可能不受影响。十毫秒内的低延迟。ZGC的RegionZGC采用了Region-based内存布局(有的资料也叫Page或ZPage)。ZGC的Region是动态的,动态创建和销毁,具有动态的region容量。在x86架构下,ZGC的Region分为大、中、小三种容量:SmallRegion(小区域):容量固定为2MB,用于放置小于256KB的小对象。MediumRegion(中型区域):容量固定为32MB,用于放置大于等于256KB但小于4MB的对象。LargeRegion(大区域):容量不固定,可以动态改变,但必须是2MB的整数倍。用于放置4B以上的大型物件。每个大Region中只会存储一个大对象。彩色指针彩色指针(ColoredPointer)是ZGC的核心技术。在了解ZGC的运行过程之前,我们先来了解一下彩色指针。ZGC的染色指针是一种直接在指针上存储少量附加信息的技术。ZGC只支持64位系统。在64位系统中,高18位不能用来寻址,剩下的46位可以用来寻址。ZGC利用46位中的高4位(ZGC在第42~45位存储对象生存信息)提取并存储4个标志位。通过这些标志位,虚拟机可以直接从指针中看到其引用对象的三色标记状态,是否进入了重分配集合(即被移动),是否只能通过finalize()方法访问。这种方式进一步压缩了只有46位的地址空间,导致ZGC可以管理的内存不能超过4TB。当一个对象被创建时,ZGC也会在M0、M1和Remapped地址空间为该对象申请一个虚拟地址,这三个虚拟地址对应同一个物理地址,但这三个空间只有一个空间在同时高效。在对象被ZGC标记的过程中,这三个空格会进行切换,切换过程在此不再赘述。这时候可达性分析遍历对象图来标记对象,也可以说是遍历“引用图”来标记“引用”。读取障碍读取障碍是JVM将一小段代码插入到应用程序代码中的一种技术。当应用程序线程从堆中读取对象引用时执行此代码。请注意,此代码仅由“从堆中读取对象引用”触发。Objecto=obj.FieldA//从堆中读取引用,需要加屏障Objectp=o//不需要加屏障,因为引用不是从堆中读取的o.dosomething()//不需要加屏障,因为引用不是从堆中读取的触发“读屏障”,如果发现对象被移动,那么“读屏障”会将读指针更新为对象的新地址,使应用线程始终访问对象的新地址。有ZGC运行过程中有四个阶段,ZGC采用mark-copy算法,ZGC的运行过程分为4个主要阶段,其中ZGC合并第一阶段Mark和最后阶段Remap(合并的原因会ConcurrentMark:遍历对象图进行可达性分析,但ZGC标记是对指针进行的,标记阶段会更新染色指针中的Marked0和Marked1标志位。标记过程是针对整个堆的。ConcurrentPrepareforRelocate(ConcurrentPrepareforRelocate):在这个阶段,需要根据具体的查询条件,计算本次收集过程中要清理哪些Region,并将这些Region组成一个分布集(RelocationSet)。ZGC每次回收时都会扫描所有Region。并发重定位(ConcurrentRelocate):重定位是ZGC执行过程的核心阶段。在这个过程中,将重分布集中存活的对象复制到新的Region中,并为重分布集中的每个Region维护一张转发表(ForwardTable),记录了旧对象到新对象的转向关系。并发重新映射:重新映射所做的是修复整个堆中重新分配集中对旧对象的所有引用。这个操作需要遍历对象图,所以ZGC将这个过程合并到下一个垃圾回收周期的并发标记阶段,节省了一次遍历对象图的开销。指针的自愈能力:在并发重分配阶段,在维护转换表的过程中,如果此时用户线程并发访问重分配集中的对象,本次访问将被预设的内存屏障拦截,并且然后立即根据Region上的转发表记录将这次访问转发给新复制的对象,同时更正和更新引用的值,使其直接指向新的对象。ZGC将这种行为称为“自我修复(Self-Healing)”能力。这样做的好处是只有第一次访问旧对象才会陷入转发,也就是只慢一次。这四个主要阶段的总停顿时间小于10ms。详细流程:ZGC的详细GC流程只有三个STW(StopTheWorld)阶段:初始标记、重新标记、初始转移。其中initialmarking和initialtransfer只需要分别扫描所有的GCRoots,处理时间与GCRoots的数量成正比,一般需要很短的时间;re-marking阶段的STW时间很短,最多1ms,超过1ms就重新进入Concurrentmarking阶段。也就是说,几乎所有的ZGC停顿都只取决于GCRoots集合的大小,停顿时间不会随着堆的大小或活动对象的大小而增加。ZGC触发的时机主要是根据分配率的自适应算法,其他的方法暂且不提。算法原理可以简单描述为“ZGC根据最近的对象分配率和GC时间,计算何时内存使用达到阈值,触发下一次GC”。日志中的关键字是“AllocationRate”一个完整的GC日志优缺点ZGC的优缺点停顿时间不超过10ms;暂停时间不会随着堆的大小或活动对象的大小而增加;支持8MB~4TB级别的堆(未来会支持16TB)ZGC适用于大内存低延迟业务的内存管理和回收。缺点:可接受的对象分配率不能太高。高对象分配率会创建大量新对象。这些新对象很难进入当前集合的标记范围,只能算是存活对象,产生大量浮动垃圾。每次回收的内存小于浮动垃圾占用的空间时,堆中的可用空间就会越来越小。