竹笋

首页 » 问答 » 灌水 » ZGC生产注意事项
TUhjnbcbe - 2023/4/29 18:33:00

4.1RSS内存异常现象

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

4.2共享内存调整

ZGC需要在sharememory中建立一个内存文件来作为实际物理内存占用,因此当要使用的Java的堆大小大于/dev/shm的大小时,需要对/dev/shm的大小进行调整。通常来说,命令如下(下面是将/dev/shm调整为64G):

vi/etc/fstabtmpfs/dev/shmtmpfsdefaults,size=M00

首先修改fstab中shm配置的大小,size的值根据需求进行修改,然后再进行shm的mount和umount。

umount/dev/shmmount/dev/shm

4.3mmap节点上限调整

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

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

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

5.ZGC在腾讯的大规模生产实践

目前TencentKonaJDK11的ZGC已经在腾讯广告大数据场景,腾讯云VPC、WAF等业务场景上长期稳定运行,并协助业务取得了优异的性能表现。

5.1支持广告海量数据查询

Hermes是腾讯自研的大数据实时分析系统,具有海量数据实时接入和存储、低延迟查询分析的特性,支持千级维度的多维分析,以及日增量万亿的海量日志接入和查询分析。在广告业务实时OLAP分析业务中,要求Hermes系统上99%的SQL查询端到端延迟不超过3s,而采用默认配置的G1GC时仅98.1%的SQL查询端到端延迟不超过3s。通过切换Kona11ZGC,SQL端到端延迟满足率上升为99.5%,同时单个SQL查询中GC造成的延迟不超过20ms。

5.2超大堆支持

腾讯VPC团队为腾讯云提供网络控制服务,该服务主要存储用于云上资源通信的网络配置等信息,并提供配置信息的查询、修改和下发等服务。业务要求在G内存的机器上尽可能多的存储配置信息(最大支持M的监听数目),并且保证压力场景下读写延迟不超过1s。采用G1GC则会高频率出现大量的延迟超过10s,通过腾讯大数据JVM团队配合切换ZGC,并解决ZGC在业务遇到的MarkStackOverflow、进入Safepoint缓慢、ZGCMark假死等问题,最终使得业务能够在压力场景下,将预期的业务存储容量提升12.5%,同时读写延迟不超过50ms。

5.3助力提升SLA

腾讯WAF团队采用Java来快速实现产品功能迭代及上线,其中,旁路安全服务是一个基于Netty框架的Http服务,此服务对时延要求很严格,需要达到99.99%端到端请求时延小于80ms的SLA目标。因此GC的STW对此服务有一定负面影响,需要进一步降低“世界暂停”时间。在使用ZGC之前,WAF团队使用的是G1GC,前期花费了大量时间对G1GC进行选项调试,并进行了代码层面的修改。但由于G1GC本身的不足,仍然存在请求抖动延迟,无法达到既定的SLA目标。在腾讯大数据JVM团队的配合下,切换ZGC之后,该业务的P请求延迟稳定小于80ms,为用户提供了更快速、稳定的服务。

6.社区回馈

腾讯大数据JVM团队在支持业务切换ZGC的同时,将遇到的相关问题和修复积极向社区报告和回馈,争做OpenJDK社区好公民。

6.1ZGC与VectorAPI联合使用问题

在广告某业务中,上线VectorAPI以提升机器学习效率,同时打开ZGC以满足服务SLA,在业务运行过程中出现结果非预期现象,并且社区存在类似的错误报告。通过对JIT生产的汇编代码进行分析,发现存在loadbarrier缺失现象。经分析,在C2的Vector优化阶段需要对Vector节点进行Unbox操作,该优化阶段会新生成一个load节点,并且又未考虑GCBarrier对load操作的影响,即ZGC需要对load操作生成loadbarrier,从而导致这个新生成的load节点缺少loadbarrier信息,最终未能生成相关barrier指令。通过对Vector优化阶段新生产的load操作增加GCbarrier处理流程,使得该阶段能够生成带gcbarrier信息的load节点,从而在不同GC选项下均能生成对应正确的barrier代码。该修复贡献给社区后以P2优先级合入JDK16。

6.2ZGCMarkStackOverflow问题

腾讯云某业务对ZGC进行灰度时,出现JVM进程崩溃现象,相关日志显示是由于ZGC标记阶段使用的MarkStack超过预先设定的8G内存导致,而通常情况下MarkStack的使用不会超过32M。经过业务全力配合,拿到一个可复现的场景进行深入分析,发现在这种场景下ZGC对于MarkStack的使用存在两个缺陷:第一,大量的MarkStack未使用满就塞入全局队列,造成单个Stack内存碎片问题;第二,大量的对象被多次压入MarkStack中,造成Stack中的Entry重复率很高,浪费Stack空间。腾讯大数据JVM团队作出快速修复,验证MarkStackOverflow问题可解后,将该问题和修复报告OpenJDK社区,社区基于提交的patch给出了更为优雅的修复方案,并将腾讯大数据JVM团队作为co-author联合提交代码入库,目前两个问题的修复均已入库JDK17。

6.3ZGCMark假死问题

在分析ZGC在业务上的表现时,需要打开gcdebuglog选项,在启动后不久,出现进程卡死现象。分析发现绝大部分gcworker线程处在“ConcurrentMarkTryTerminate”阶段,并且在等待“ConcurrentMarkIdle”阶段的log文件读写锁,另外一个gcworker线程处于写log过程中,由此可以分析出由于gcworker线程均在抢log文件锁,导致gcworker线程最终形成一种动态死锁状态,即所有的gcworker线程均处于“等锁-拿锁-释放锁”这种无限循环中。这种假死现象是由于ZGC的ConcurrentMark退出机制导致的,在退出机制中所有的gcworker线程会等待1ms来进行状态同步,而等待结束后会进行相关log打印,这个打印需要前述log文件锁,从而导致动态假死现象出现。腾讯大数据JVM团队快速修复该问题,并提交社区,目前该贡献已合入JDK17中。

1
查看完整版本: ZGC生产注意事项