fastjson的内存泄漏问题

4 篇文章 0 订阅

最近碰到了两个内存泄漏问题,都是fastjson引起的,因此这里做一下简单记录,一个是序列化引起,另外一个是反序列化引起

序列化问题导致堆外metaspace内存泄漏

问题代码如下:

            SerializeConfig config = new SerializeConfig();
            config.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
            String msg = JSONObject.toJSONString(pushMsg, config);
            ProducerResult producerResult = processor.sendMessage(msg);

主要使用到了SerializeConfig这个类,造成的影响就是metaspace内存增加。原因是SerializeConfig的构造方法中默认会使用asm,然后创建ASMSerializerFactory,然后创建代理类,如果每次都new一个新的SerializeConfig,就会导致频繁创建代理类(如com.alibaba.fastjson.serializer.ASMSerializer_1_XXX),而metaspace保存的就是类的元信息,就会造成metaspace增加

        try {
            if (this.asm) {
                this.asmFactory = new ASMSerializerFactory();
            }
        } catch (Throwable var4) {
            this.asm = false;
        }
。。。
        JavaBeanSerializer serializer = this.asmFactory.createJavaBeanSerializer(beanInfo);
。。。
createJavaBeanSerializer中
                String className = "ASMSerializer_" + this.seed.incrementAndGet() + "_" + clazz.getSimpleName();

解决办法:

将SerializeConfig作为类的静态变量

 
private static final SerializeConfig CONFIG = new SerializeConfig();
 
static {
    CONFIG.propertyNamingStrategy = PropertyNamingStrategy.SnakeCase;
}
反序列化问题导致堆内存泄漏

问题代码如下:

return JSONObject.parseObject("xxx",
                new TypeToken<Set<String>>() {
                }.getType());

分析内存一般是 com.alibaba.fastjson.util.IdentityHashMap$Entry对象比较大,或者是

com.alibaba.fastjson.parser.ParserConfig

原因:

new TypeToken<Set<String>>() {

}.getType() 这个每次都会生成一个type,而DefaultJSONParser在反序列化时使用到了ParserConfig的getDeserializer方法来获取反序列化器,get获取时key就是type,而此处是根据hashcode去取,就导致每次都无法命中,每次都会新生成一个序列化器,导致内存越来越大

            
DefaultJSONParser parser = new DefaultJSONParser(input, config, featureValues);
 //parseObject中 根据type获取反序列化器
ObjectDeserializer deserializer = this.config.getDeserializer(type);
// 具体get方法
    public final V get(K key) {
        int hash = System.identityHashCode(key);
        int bucket = hash & this.indexMask;

        for(IdentityHashMap.Entry entry = this.buckets[bucket]; entry != null; entry = entry.next) {
            if (key == entry.key) {
                return entry.value;
            }
        }

        return null;
    }
// 注释内容:无论给定的x对象是否覆盖了hashCode()方法,都会调用默认的hashCode()方法返回hashCode,如果x == null, 返回0。
    // 这个默认的hashCode()方法就是Object类中的hashCode方法。
    // 这说明默认的hashCode方法是根据对象的地址转换所得到的。每次new出来的新对象的地址都是不同,所以会出现这个问题
 /**
     * Returns the same hash code for the given object as
     * would be returned by the default method hashCode(),
     * whether or not the given object's class overrides
     * hashCode().
     * The hash code for the null reference is zero.
     *
     * @param x object for which the hashCode is to be calculated
     * @return  the hashCode
     * @since   JDK1.1
     */
    public static native int identityHashCode(Object x);
    
    
可能使用到的命令

jps 找到服务相关的pid

jmap查看JVM对象信息

jmap -histo pid

增加jvm启动参数:-XX:+TraceClassLoading -XX:+TraceClassUnloading 查看项目的类加载日志

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值