ZGC是从JDK11中引入的一种新的支持弹性伸缩和低延迟垃圾收集器,ZGC可以工作在KB~TB的内存之下,作为一种并发的垃圾收集器,ZGC保证应用延迟不会超过10毫秒(即便在堆内存很大的情况下),在JDK11中是以实验阶段的特性被发布出来的,到JDK13时,ZGC可以支持到16TB的堆内存,并且可以将未提交的内存归还给操作系统。
为什么引入ZGC
JVM的自动垃圾收集虽然减少了开发人员的工作,在一定程度上减少了内存泄漏的风险,但是由于GC是自动进行的,一些无法预知的事情有时候可能产生对应用有害的影响。
- 延迟增加导致应用的吞吐量和性能
随着时代发展,硬件会逐渐便宜,应用使用的内存将会越来越大,但是又不能增加延迟,降低吞吐量
ZGC保证,不管在什么情况下,延迟不会超过10毫秒。
The Z Garbage Collector, also known as ZGC, is a scalable low latency garbage collector designed to meet the following goals:
- Pause times do not exceed 10ms
- Pause times do not increase with the heap or live-set size
- Handle heaps ranging from a few hundred megabytes to multi terabytes in size
ZGC特性
ZGC最典型的特性是它是一款并发(concurrent)的GC,其它的特性如下:
- 它可以标记内存,复制和迁移(relocate)内存,所有的操作都是并发的,同时它有一个并发的引用处理器
- 其它的垃圾收集器都是使用
store barriers
,ZGC使用load barriers
,用于跟踪内存- lock->unlock->read->load 读内存
- use->assign->store->write 写内存
- ZGC可以更加灵活的配置大小和策略,相比于G1,它可以更好的处理非常大(very large)对象的释放
- ZGC只有一代,没有新生代,老年代什么的,但是ZGC可以支持局部压缩,在内存恢复和迁移(reclaim and relocate)时,ZGC仍然有很高的性能
- ZGC依赖NUMA-aware(非均衡存储器访问),需要我们的内存支持这种特点
特性进度表
- JDK11,2018年 9月
- ZGC发布
- 不支持类的卸载。-XX:+ClassUnloading 不生效
- JDK12 2019年 3月
- 支持并发的类卸载
- 暂停时间进一步缩短
- JDK13 2019年 9月
- 最大堆内存从4TB -> 16TB
- 支持归还未使用的内存 uncommitting unused memory
- 支持Linux与/AArch64平台
- 减少时间到一个固定的时间点之下 (Reduced Time-To-Safepoint)
- 支持 -XX:SoftMaxHeapSize ,当设置这个参数的时候,ZGC会尽量在指定的内存大小之下,除非为了避免内存溢出:参考:https://bugs.openjdk.java.net/browse/JDK-8222487
- JDK 14 计划在2020年3月
- 增加稳定性
- 支持不连续的地址空间
- ...
ZGC支持的平台:
平台 | 是否支持 | 当前进度 |
---|---|---|
Linux/x64 | Y | Since JDK 11 |
Linux/AArch64 | Y | Since JDK 13 |
macOS | In Progress | |
Windows | In Progress |
垃圾收集原理
几个术语:
- parallel 多个垃圾收集线程在一起工作,应用可能会停止
- serial 垃圾收集器只有一个线程在工作
- stop the world 应用程序停止
- concurrent 垃圾收集器在后台运行,应用程序同时也在运行
- incremental 在垃圾收集工作结束之前,先停止垃圾收集,等一会再过来完成剩下的工作
ZGC引入了两个新的概念,pointer coloring和load barriers.
Point Coloring
这个特性让ZGC能够发现,标记,定位和重新映射对象,它只能工作在64位的操作系统上,实现colored pointer需要虚拟地址(virtual address masking)。
- finalizable 对象可以被finalizer到达
- marked0 和marked1 标记可达的对象
- remap 引用指针到当前对象的地址,对象可能会被relocate,这个地址表示对象被relocate
Load Barrier
load barrier是一段代码,当线程从堆中加载引用的时候被运行。例如,当我们访问对象的一个非主要类型的属性。
在ZGC中,load barrier检查引用的元数据位,根据元数据位对引用的对象做一些处理,因此可能在我们获取对象的时候对象的引用会被修改掉,但是不影响我们的使用。
实战
可以从jdk官网下载最新版本的JDK,下载地址:
https://www.oracle.com/technetwork/java/javase/downloads/jdk13-downloads-5672538.html
快速开始
GC日志标记格式如下:
-Xlog:,[, ...]:
只是想要查看ZGC是否生效:
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xmx -Xlog:gc
想要查看更加详细的ZGC日志信息:
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xmx -Xlog:gc*
将更详细的日志信息记录在文件中:
-XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xmx -Xlog:gc*:gc.log
GC调参
通用GC选项 | ZGC选项 | ZGC 诊断选项-XX:+UnlockDiagnosticVMOptions |
---|---|---|
-XX:MinHeapSize, -Xms -XX:InitialHeapSize, -Xms -XX:MaxHeapSize, -Xmx -XX:SoftMaxHeapSize -XX:SoftRefLRUPolicyMSPerMB | -XX:ZAllocationSpikeTolerance -XX:ZCollectionInterval -XX:ZFragmentationLimit -XX:ZMarkStackSpaceLimit -XX:ZPath -XX:ZUncommit -XX:ZUncommitDelay | -XX:ZProactive -XX:ZStatisticsInterval -XX:ZVerifyForwarding -XX:ZVerifyMarking -XX:ZVerifyObjects -XX:ZVerifyRoots -XX:ZVerifyViews |
是否启用NUMA支持:
# 启用NUMA
-XX:+UseNUMA
# 停用NUMA
-XX:-UseNUMA
调整并发的线程数:
-XX:ConcGCThreads=
返回未提交的内存到操作系统,堆内存不会地址设置的最小堆内存-Xms
# 多久未提交的内存会返回给系统
-XX:+ZUncommit -XX:ZUncommitDelay=
开启大分页,一般会带来更好的性能提升,吞吐量,延迟和启动时间都有所改善。并没有看到明显的缺点
- 大分页在Linux系统中一般为2MB的大小,如果有16G的堆内存,那么意味着需要16GB/2MB = 8192个大分页,以下命令需要 Linux kernel >= 4.14
# 配置操作系统中的分页池数量
echo 9216 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
# 查看系统中现在的分页数量
cat /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages
-XX:+UseLargePages
- 如果Linux kernel < 4.14,那么如下方式
mkdir /hugepages
mount -t hugetlbfs -o uid=123 nodev /hugepages
java -XX:+UnlockExperimentalVMOptions -XX:+UseZGC -Xms16G -Xmx16G -XX:+UseLargePages
开启透明分页,透明分页可能导致延迟上的一些问题,有时候不推荐使用,开启透明分页需要Linux kernel < 4.7
# 开启透明分页
echo madvise > /sys/kernel/mm/transparent_hugepage/enabled
echo advise > /sys/kernel/mm/transparent_hugepage/shmem_enabled
-XX:+UseTransparentHugePage
最后
ZGC仍然是处于试验特性阶段,但其保证延迟时间不低于10ms的特性,以及当前对堆内存大小的支持力度,还是值得一试,让我们期待ZGC成为一款更加优越的垃圾收集器吧。
参考
- OpenJDK Wiki
- ZGC 介绍
- JDK11 之ZGC