背景
闲鱼某关键应用A依赖类目系统富客户端(下文简称类目客户端),旨在为闲鱼商品域其他应用提供各类商品类目及属性数据(下文简称CPV数据)查询服务。
每天凌晨,该应用所依赖的类目富客户端执行新老版本数据包切换时,应用提供的服务抖动非常明显,表现为大量接口超时(耗时100ms -> 3-5s),服务成功率明显下降(100% -> ~92%),RPC线程池活跃线程数上涨(50 -> ~100),抖动最长可持续20s,影响到商品发布、商品详情页等依赖CPV数据的关键业务场景;且夜间发生抖动,时间点不固定,抖动发生时开发同学也难以关注到,影响面较为不可控,因此需要排查并彻底解决此问题。
排查过程
其实这是一个表象很简单,但是根因藏得比较深的问题,笔者在排查过程中也走了一些弯路,也一并写出来供读者作为前车之鉴的参考。
堆空间不够?
结构化应用线上原先使用的是4C8G的标准规格容器,分配4G内存作为堆内存,截取部分JVM启动参数如下:
-Xms4g -Xmx4g -XX:MetaspaceSize=512m -XX:MaxMetaspaceSize=512m -XX:+UnlockExperimentalVMOptions -XX:G1MaxNewSizePercent=65 -XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=55 -XX:G1HeapRegionSize=16m -XX:G1NewSizePercent=25 -XX:MaxGCPauseMillis=120 -XX:+ParallelRefProcEnabled -XX:MaxDirectMemorySize=1g -XX:+TraceG1HObjAllocation -XX:ReservedCodeCacheSize=512m -XX:+UseCodeCacheFlushing
据反馈接口抖动的同学描述,在接口抖动的时间点,请求失败的机器发生了FGC。
undefined
由于本人先前在排查类似的FGC问题时,经常能在Heap Dump的支配树中看到类目客户端相关对象长期占据高位,是内存占用大户。所以难免主观印象先入为主,初步以为是切换版本的过程中,在老版本数据包卸载之前,可能会存在短暂的堆内空间占用double的情况。
而启动参数配置Eden区下限为25%。可能存在 老年代常驻占用 + 新数据包 > 4G 的情况引发FGC。
因此觉得 既然空间占用double不可避免,老年代常驻的堆空间短时间内又不可能显著下降,Eden区为了解决之前该应用发生Mixed GC时Eden区反常缩小导致YGC异常,又必须固定一个下限值,那只能是把容器内存规格扩大看看能否缓解 。
于是将应用容器基线升级到4C16G,设置在16G容器环境下,分配10G内存给堆空间,并逐步升级线上服务的容器规格。
结果花了两天时间,升级了线上大部分的容器后,查看监控,发现大规格的容器确实没有FGC了,且得益于超大的堆内存(10G),从GC监控上看甚至看不出有切包的动作。但是,RPC成功率还是会下跌(100% -> 97%),且RPC线程池线程数还是有少量上涨(50 -> ~71)。
由此可见,FGC并不是切包抖动的核心成因,