关于一次full gc定位(SerializeConfig相关)

1.背景

由于原先推送二方包的能力不支持扩展,现在由于业务发展需要增加新能力。所以重新开发了消息starter

开发思路是通过抽象工厂去分别创建安卓和IOS的对象。

上线后发现每日固定的时间点会出现full gc的情况,该时间段为推送业务高峰期,所以推断出问题是由于新上的推送starter引起的。

从jvm监控可以观察到,某时间段full gc频率非常高,同时堆内存使用主要集中在eden区。由此可推断出是推送业务中新创建对象太多所导致。

同时最近关于推送,改动的只有推送starter,那么我们基本可以把目标放在stater上面。

2.问题初步分析

尝试导出类加载日志进行分析

打开后未发现排名靠前的相关类,一切都很正常,唯一值得怀疑的是fastjson.asm相关的。继续查找sterter相关类。发现了些比较奇怪的类加载。但也暂时没看出啥问题。

类加载既然看着正常,为什么线上业务高峰期还会频繁full gc?

3.模拟线上业务排查jvm

操作方式是新建一个starter调用的junit Test,循环调用2000次。然后启动VisualVM进行排查

堆的波动看起来比较规律,也没有想象中那种堆内存无限飙高的情况出现。但正常的情况是一个较为平稳的波段,而不是这样断崖式起伏。

此时考虑是否是与三方交互的HttpClient引起的呢? 假如http请求时间较长,那么类在内存中就会一直被引用,就无法被释放。 此时尝试把调用三方的doPost注释掉。

要跑5分多钟的程序这次只用了36秒就跑完了,那么此时是不是可以怀疑是httpClient这里出了问题?

查询相关资料后发现httpClient是可以重用的,不需要重复的去创建,这样每次打开关闭都会有相应的性能损耗。对httpClient进行了一顿优化后,再次进行测试。

发现性能大概优化了50%,但感觉还是不够。堆的图形表现并没有明显的差别。

此时再把目光转回类加载信息当中,也就是fastJson的serializer中。查看了转json的JsonUtil代码

public static <T> String toJsonWithSnakeCase(T objects){
    SerializeConfig config = new SerializeConfig();
    config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
    return JSON.toJSONString(objects,config);
}

发现这段代码很可疑,怀疑是不是这里的问题呢?

对以上代码进行优化后再次Test进行尝试

    private static SerializeConfig config;

    static {
        config = new SerializeConfig();
        config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
        ParserConfig.getGlobalInstance().propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;

    }

    public static <T> String toJsonWithSnakeCase(T objects){
        return JSON.toJSONString(objects,config);
    }

修改后对应jvm的堆内存情况

执行用时1分59秒,并且堆的加载图表也正常了,问题被成功定位!

4.问题复盘

虽然问题解决了,但还是该想想为什么区区一行转json字符串的代码却造成了线上频繁的full gc。

我们先来看下new SerializeConfig()干了些啥事。

1.初始化容器。

2.创建ASM工厂。

3.初始化序列化器。

简简单单一行代码,内部竟然要加载这么多东西,这jvm能好么~

我们接着往下看最终调用的JSON.toJSONString(objects,config)方法,直接定位到最终执行代码的那个实际方法

我们重点来看write这里。

需要线获取一个ObjectSerializer才能去write,接着进去看。

发现取ObjectSerializer的时候会先从mixInSerializers这个容器中去取,而这个容器所属的类是SerializeConfig,也就是我们每次去新建的这个类,所以就相当于每次都要从ASM的工厂去创建,好吧,汪汪队倒大霉。

而接着往下看就会发现调用到createASMSerializer这个方法,而这个方法里会有这么一个调用,asmFactory.createJavaBeanSerializer,也就是开头我们重复创建的ASMSerializerFactory。

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值