介绍
ZGC(Z Garbage Collector) 是一款性能比 G1 更加优秀的垃圾收集器。ZGC 第一次出现是在 JDK 11 中以实验性的特性引入,这也是 JDK 11 中最大的亮点。在 JDK 15 中 ZGC 不再是实验功能,可以正式投入生产使用了,使用 –XX:+UseZGC 可以启用 ZGC。
特征
- 停顿时间(STW)不超过10ms(JDK16是不超过1ms),且不会随着堆的大小增加而增加
G1 300ms
- 理论上最大支持 16TB 的大堆,最小支持 8MB 的小堆。2048 分区,堆128G还是负担比较重
- 跟 G1 相比,对应用程序吞吐量的影响小于15%,吞吐量,通过扩容解决
目标
主要目标:巩固老大地址、卷其他语言!
- 抢C++的市场
- 防止被go语言等抢市场
ZGC的案例介绍
阿里(借鉴ZGC优化自己JVM)
美团(规则平台等)
58(Hbase平台)
腾讯(在线交互、竟价广告、量化交易等)
堆空间分页模型(无分代)
将内存分为三种类型的页面
- 小型Region(Small Region):容量固定为2MB, 用于放置小于256KB的小对象。
- 中型Region(Medium Region):容量固定为32MB, 用于放置大于等于256KB但小于4MB的对象。
- 大型Region(Large Region):容量不固定, 可以动态变化, 但必须为2MB的整数倍, 用于放置4MB或以上的大对象。 和操作系统有关
页面类型 | 页面大小 | 页面内对象的大小 | 页面内对象对齐的粒度 |
小页面 | 2MB | 小于等于256KB | MinObjectAlignmentInBytes |
中页面 | 32MB | 在256KB和4MB之间 | 4KB |
大页面 | X*MB,受操作系统控制 | 大于4MB | 2MB |
ZGC对于不同页面回收的策略也不同。简单地说,小Region优先回收;中Region和大Region则尽量不回收。
为什么这么设计?
Linux Kernel 2.6引入的标准大页(huge page)
标准大页(huge page)是Linux Kernel 2.6引入的目的是通过使用大页内存来取代传统的4KB内存页面,以适应越来越大的系统内存,让操作系统可以支持现代硬件架构的大页面容量功能
ZGC的内存布局
应用程序创建对象时,首先在堆空间申请一个虚拟地址,但该虚拟地址并不会映射到真正的物理地址。ZGC同时会为该对象在M0、M1和Remapped地址空间分别申请一个虚拟地址,且这三个虚拟地址对应同一个物理地址,但这三个空间在同一时间有且只有一个空间有效。ZGC之所以设置三个虚拟地址空间,是因为它使用“空间换时间”思想,去降低GC停顿时间。“空间换时间”中的空间是虚拟空间,而不是真正的物理空间
虚拟内存映射
NUMA
统一内存访问UMA:
- 在x86架构中,内存没有整合进CPU,需要总线来访问
- 任何CPU访问内存的速度都是一致的,没有差异,称为统一内存访问Uniform Memor Access, UMA)
- 当所有核都链接到一块内存上时,访问内存中任何一个区域的时间都相同
NUMA:
核访问与自己直接相连的内存区域,比访问其它区域快的多,因为访问其它区域需要通过另外一个芯片,因此被称为非一致性内存访问系统NUMA(Nonuniform Memory Access)
UMA系统中因为核直接和内存相连,任意一个核都能访问内存中的任何地址,因此当数据存储在内存中不同位置时,UMA系统存取指令和数据更快。NUMA系统中因为内存可以连接到不同的内存上,因此具有更好的内存扩展性。
因为在NUMA系统中想要存取不同内存上的数据时,需要核心直接交互才能实现,跨NUMA会导致几纳秒的时间浪费,因此如果程序对性能比较敏感需要将程序绑定到指定的NUMA上,以此来避免不同NUMA之间核的交互
ZGC为什么那么快?
分代模型和分区模型
•低延迟:
ZGC的分页模型允许并发地处理内存分配和回收操作,从而减少了垃圾收集的停顿时间。相比之下,分代模型需要在不同代之间进行对象的复制或移动,可能会导致更长的停顿时间。
•内存利用率高:
ZGC的分页模型可以动态地调整页的大小,以适应不同大小的对象。这样可以提高内存的利用率,减少内存碎片的产生。而分代模型中,不同代的内存空间是固定的,可能会导致内存碎片的问题。
•可伸缩性:
ZGC的分页模型允许将堆内存划分为多个页区,并且每个页区都有独立的垃圾收集线程。这样可以实现垃圾收集的并行性,提高系统的可伸缩性和吞吐量。而分代模型中,不同代的垃圾收集是串行或并发-串行的,可能无法充分利用多核处理器的性能。
•适应大内存堆:
ZGC的分页模型可以有效地管理大内存堆。它可以根据需要动态地增加或减少页的数量,以适应大内存堆的需求。而分代模型中,不同代的内存空间是固定的,无法有效地管理大内存堆。
GC标记信息位置的变化
传统垃圾回收器通过扫描堆中的对象(扫描堆空间是很慢的),根据对象头中的可达性标记信息,来确定对象是否应该被回收。
ZGC不直接依赖于对象头中的信息来进行垃圾回收决策,而是把GC信息存在内存引用地址上。GC时通过扫描栈上的内存引用指针来确定对象的引用关系和可达性,从而来判断对象是否应该被回收。
垃圾回收流程
标记阶段(标识垃圾)
转移阶段(对象复制或移动)
初始阶段:在ZGC初始化之后,此时地址视图为Remapped,程序正常运行,在内存中分配对象,满足一定条件后垃圾回收启动。
初始标记:初始标记只需要扫描所有GC Roots,其处理时间和GC Roots的数量成正比,停顿时间不会随着堆的大小或者活跃对象的大小而增加。
并发标记:扫描剩余的所有对象,这个处理时间比较长,所以走并发,业务线程与GC线程同时运行。但是这个阶段会产生漏标问题。
基于上一步的绿色节点开始并发标记:所有访问到的活对象都标记为绿色,指针也是绿色
再标记:主要处理漏标对象,通过SATB算法解决(G1中的解决漏标的方案)
主要解决并发标记过程中的漏标问题,活的对象和指针仍然是绿色,可以发现,再标记之后,仍然是蓝色的对象就是垃圾对象
- 并发转移准备:分析最有价值GC分页哪些区域有垃圾,哪些需要回收等等
- 初始转移:转移初始标记的存活对象同时做对象重定位,初始标记的对象,就是初始转移的对象,GC ROOTS
并发转移:对转移并发标记的存活对象做转移,经过并发标记和再标记调整之后的对象,就是并发转移的对象
在两次GC中间业务线程如何访问没有做完重定位的对象?
参数
If you're trying out ZGC for the first time, start by using the following GC options:
-XX:+UseZGC -XX:+ZGenerational -Xmx<size> -Xlog:gc |
For more detailed logging, use the following options:
-XX:+UseZGC -XX:+ZGenerational -Xmx<size> -Xlog:gc* |
堆大小:Xmx。当分配速率过高,超过回收速率,造成堆内存不够时,会触发 Allocation Stall,这类 Stall 会减缓当前的用户线程。因此,当我们在GC日志中看到 Allocation Stall,通常可以认为堆空间偏小或者 concurrent gc threads 数偏小。
GC 触发时机:ZAllocationSpikeTolerance, ZCollectionInterval。ZAllocationSpikeTolerance 用来估算当前的堆内存分配速率,在当前剩余的堆内存下,ZAllocationSpikeTolerance 越大,估算的达到 OOM 的时间越快,ZGC 就会更早地进行触发 GC。ZCollectionInterval 用来指定 GC 发生的间隔,以秒为单位触发 GC。
GC 线程:ParallelGCThreads, ConcGCThreads。ParallelGCThreads 是设置 STW 任务的 GC 线程数目,默认为 CPU 个数的 60%;ConcGCThreads 是并发阶段 GC 线程的数目,默认为 CPU 个数的 12.5%。增加 GC 线程数目,可以加快 GC 完成任务,减少各个阶段的时间,但也会增加 CPU 的抢占开销,可根据生产情况调整。
General GC Options | ZGC Options | ZGC Diagnostic Options (-XX:+UnlockDiagnosticVMOptions) |
-XX:MinHeapSize, -Xms -XX:InitialHeapSize, -Xms -XX:MaxHeapSize, -Xmx -XX:SoftMaxHeapSize -XX:ConcGCThreads -XX:ParallelGCThreads -XX:UseDynamicNumberOfGCThreads -XX:UseLargePages -XX:UseTransparentHugePages -XX:UseNUMA -XX:SoftRefLRUPolicyMSPerMB -XX:AllocateHeapAt | -XX:ZAllocationSpikeTolerance -XX:ZCollectionInterval -XX:ZFragmentationLimit -XX:ZMarkStackSpaceLimit -XX:ZProactive -XX:ZUncommit -XX:ZUncommitDelay | -XX:ZStatisticsInterval -XX:ZVerifyForwarding -XX:ZVerifyMarking -XX:ZVerifyObjects -XX:ZVerifyRoots -XX:ZVerifyViews -XX:ZYoungGCThreads -XX:ZOldGCThreads -XX:ZBufferStoreBarriers |
标签:标记,对象,XX,GC,垃圾,JVM,ZGC,内存 From: https://blog.csdn.net/qq_26594041/article/details/139382160