Skip to content

ZGC使用手册

王超 edited this page Jun 24, 2021 · 2 revisions

常见参数

ZAllocationSpikeTolerance

此参数根据当前分配速率进行GC触发,以及时回收内存,满足Java线程的内存分配需求。

默认值为2,即最大容忍MaxAllocRate * 2的分配速率。可根据应用剩余内存和最大分配速率来进行调整,使应用不发生Allocation Stall.

用法:如需要容忍MaxAllocRate * 6的分配速率,

-XX:ZAllocationSpikeTolerance=6

ZCollectionInterval

此参数根据时间进行GC触发,单位为s。默认不打开

用法:如需要每隔10s进行一次gc触发

-XX:ZCollectionInterval=10

ParallelGCThreads和ConcGCThreads

为常见GC参数,用于调整在STW和Concurrent阶段中使用的gc线程数目。ParallelGCThreads默认值为NumOfCPU * 0.6, ConcGCThreads默认值为NumOfCPU * 0.125

用法:如STW使用40个GC线程,Concurrent阶段使用10个GC线程

-XX:ParallelGCThreads=40 -XX:ConcGCThreads=10

ZPath

堆分配的地方。在JDK11之后更为AllocateHeapAt. 默认在/dev/shm中进行堆物理内存的分配。

用法:如在/smallpages目录作为小页内存目录挂载点,并需要在该目录下进行堆的内存文件分配

-XX:ZPath=/smallpages

GC Log参数Xlog

在jdk11之后,GC Log进行了很大的重构,详细参见jep-158: https://openjdk.java.net/jeps/158

如需要调试GC,常见的GC log参数为,下面为打开gc的debug信息、safepoint的info信息,并且指定输出文件为当前目录下的gc.log文件,同时打印日志的时间、级别和标签,日志的大小设置为最大5g。

-Xlog:gc*=debug,safepoint*=info:file=gc.log:time,level,tags:filesize=5g

注意事项

当前版本ZGC不分代

ZGC在目前还未实现分代功能,没有Young区,因此所有的GC均为Full GC,并且不需要设置Young区大小。

RSS内存异常现象

ZGC采用多映射multi-mapping的方法实现了三份虚拟内存指向同一份物理内存。而Linux统计进程RSS内存占用的算法是比较脆弱的,这种多映射的方式并没有考虑完整,因此根据当前Linux采用大页和小页时,其统计的开启ZGC的Java进程的内存表现是不同的。在内核使用小页的Linux版本上,这种三映射的同一块物理内存会被linux的RSS占用算法统计3次,因此通常可以看到ZGC的Java进程的RSS内存膨胀了三倍左右,但是实际占用只有统计数据的三分之一,会对运维或者其他业务造成一定的困扰。而在内核使用大页的Linux版本上,这部分三映射的物理内存则会统计到hugetlbfs inode上,而不是当前Java进程上。

小页内存目录大小调整

ZGC需要在share memory中建立一个内存文件来作为实际物理内存占用,因此当要使用的Java的堆大小大于/dev/shm的大小时,需要对/dev/shm的大小进行调整。

用法:如需将/dev/shm调整为64G

vi /etc/fstab
#添加如下内容:
tmpfs /dev/shm tmpfs defaults,size=65536M 0 0

然后

umount /dev/shm
mount /dev/shm

mmap节点上限调整

ZGC的堆申请和传统的GC有所不同,需要占用的memory mapping数目更多,即每个ZPage需要mmap映射三次,这样系统中仅Java Heap所占用的mmap个数为(Xmx / ZPageSize) * 3,默认情况下ZPageSize的大小为2M。

而为了给JNI等native模块中的mmap映射数目留出空间,内存映射的数目应该调整为(Xmx / ZPageSize) * 3 * 1.2

默认的系统memory mapping数目由文件/proc/sys/vm/max_map_count指定,通常数目为65536,当给JVM配置一个很大的堆时,需要调整该文件的配置,使得其大于(Xmx / ZPageSize) * 3 * 1.2

大页设置

为了减少TLB miss和Kernel MM的影响,在大堆时通常推荐使用大页,这样只会增加应用的性能,而不会有其他负面影响,但是需要root权限对系统进行配置。

通常采用开启大页的命令如下, 如需要开启16G的堆,在linux下大页的大小为2M,因此需要16G/2M=8192个大页。同时,其他数据结构也需要使用大页,如预留2G给其他结构,则需要9192个大页。通过如下命令,可调整大页的数目,

echo 9216 > /sys/kernel/mm/hugepages/hugepages-2048kB/nr_hugepages

在linux 4.14之前,还需要配置大页的挂载点,

mkdir /hugepages
mount -t hugetlbfs nodev /hugepages
chmod 777 /hugepages

最终,java的启动选项为,

-XX:+UseLargePages -XX:ZPath=/hugepages

AllocationStall

ZGC的STW暂停时间为ms级别,但是由于当前不分代,支持的分配速率上限相对于分代GC较低,因此当内存不足时,如果java线程需要分配内存,则会导致该线程出现Allocation Stall.

如出现Allocation Stall,可在GC log中找到如下信息:共发生了10143次Allocation Stall, 平均暂停java线程47ms,最大暂停1127ms

[13017.500s][info   ][gc,stats    ]    Critical: Allocation Stall                              0.000 / 0.000        28.929 / 316.882      46.540 / 1126.614     46.540 / 1126.614    ms
[13017.500s][info   ][gc,stats    ]    Critical: Allocation Stall                                  0 / 0                18 / 6628             58 / 10143            58 / 10143       ops/s

如果不出现Allocation Stall,则上面的数据均为0.

通常需要对GC的触发进行调整,以及增加堆大小来消除Allocation Stall。