记一次matespace导致的FGC导致接口性能尖刺

440 阅读2分钟

这是我参与2022首次更文挑战的第13天,活动详情查看:2022首次更文挑战

0. 背景

自己服务一个核心接口的TP99有性能尖刺,开始的排查之旅。

先用skywalking定位了下执行慢的请求,发现依赖的数据库、redis组件均是快速返回。时间都占用'spingmvc'本身。

这就有点难了,性能尖刺一般怀疑db慢查询。目前确实没有。该接口QPS挺高的,想着是不是线程不够用呀。用 jstat看了下线程情况,发现tomcat的线程还是够用的。不过还是主动增大了线程数量,且增加了几个节点副本。发现并无效果。

排查进入困难之后,想着是不是有GC的STW导致请求尖刺。于是查了机器上的gc log。好家伙,既然有FGC,而且频率还不低(正常情况是避免FGC的,FGC全过程都是STW的)。

我们线上虽然用的G1,但是FGC是使用serial old。而且是GC整个堆。

1.定位FGC根因

看gc日志,是matespace触发FGC的,jdk8的matespace里放的就是class的信息,按理说这个地方的使用量是比较够用的,且不会一直增长。

而且看了下配置,matespace的初始和最大都设置了512M大小,不过还是抱着是不是真的不够用的想法扩大了max matespace,结果还是不行,fgc依旧。

接下来就想看看都有啥class被加载了,导致一直增长。(注意不是堆触发的fgc,不要想着对堆内存进行分析,友情提示一下,线上在提供服务的节点,不要轻易dump,dunp前会先进行fgc,会造成长时间的stw,请注意)。

在线上增加了

-XX:+TraceClassLoading -XX:+TraceClassUnloading -verbose:class

参数,查看类加载情况,注意这个日志是打印在控制台的。

上线后观察到有很多 fastjson.serializer.ASMServializer_****的类加载。 在网上搜了下,资料还挺多。读者也可以去搜搜。

    SerializeConfig config = new SerializeConfig();
    config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;

根因就是如上代码所致,每次toJSON时,都new SerializeConfig()。导致每次都使用了新的工厂,重新进行load class.

看了下项目有个请求三方接口的地方,入参toJSON时,果然每次都new 了config。

2.解决

解决方式就比较简单了,将SerializeConfig设置为静态(或者单例)即可。

3.验证

将改动上线后,可以看到load class情况变得非常正常了,而且再也没发生过fgc。TP99的尖刺少了超级多,当然还有稍微的几个尖刺,还需要继续排查。

如果基础设施比较健全的话,应该对FGC有相关报警的,要对线上FGC及时处理。