[alibaba/fastjson]parseObject是否存在内存泄漏情况

2025-11-13 992 views
0

当使用com.alibaba.fastjson.JSON:public static <T> T parseObject(byte[] bytes, Type clazz, Feature... features) 这个API,当传入的Type为ParameterizedTypeImpl类型时候,发生内存泄漏。

下面是我的代码:

    UserInfo userInfo=new UserInfo();
    userInfo.setName("zyr");
    userInfo.setPassword("123");
    WrapReturn wrapReturn = new WrapReturn();
    wrapReturn.setResult(userInfo);
    byte[] bytes = JSON.toJSONBytes(new WrapReturn(userInfo));
    while (true){
        Object o = JSON.parseObject(bytes, new ParameterizedTypeImpl(new Type[]{UserInfo.class}, null, WrapReturn.class));
    }

指定堆大小为100MB,运行一段时候后,出现OOM。

查看代码,发现关键调用是:com.alibaba.fastjson.parser.ParserConfig :public ObjectDeserializer getDeserializer(Type type)

public ObjectDeserializer getDeserializer(Class<?> clazz, Type type) {
    ObjectDeserializer derializer = deserializers.get(type);
    if (derializer != null) {
        return derializer;
    }
    ........
    ........
    ........
    putDeserializer(type, derializer);

    return derializer;

deserializers这个变量是一个IdentityHashMap实例,该实例并未采用hashCode来定位bucket,而采用System.identityHashCode来。所以产生上述问题。

虽然,我们多次new ParameterizedTypeImpl实例,但是这些实例都是表示同一个泛型化类型,所以在逻辑上都是相等的。那么出现上述的问题,应该是不合理的。

出现这个问题,是BUG还是使用上不规范?

回答

6

用了1.2.37,还是存在这个问题。

5

ParameterizedTypeImpl 这个你能自己缓存起来么?或者静态化做成员变量,或者使用TypeReference

2

TypeReference在我这里不适用,因为不确定泛型的具体参数。所以这里用了ParameterizedTypeImpl ,我暂时是自己做了缓存ParameterizedTypeImpl处理。我想知道这里为什么采用IdentityHashMap来缓存类型,而不采用其他的Map。

3

@zhuyiren 是为了性能哈

6

@zhuyiren 我跟你遇到了同样的问题。你自己缓存ParameterizedTypeImpl的话,总归有个缓存的上限把?如果类型很多,岂不是自己缓存也会占很大内存?

0
static ConcurrentMap<Type, Type> classTypeCache
            = new ConcurrentHashMap<Type, Type>(16, 0.75f, 1);
@Test
public void fixLeak(){
    Student student=new Student();
    student.setName("1");
    CacheWrapper cacheWrapper = new CacheWrapper();
    cacheWrapper.setCacheObject(student);
    byte[] bytes = JSON.toJSONBytes(cacheWrapper);
    while (true){
        Type argkey = new ParameterizedTypeImpl(new Type[]{Student.class}, CacheWrapper.class.getDeclaringClass(), CacheWrapper.class);
        Type cachedType = classTypeCache.get(argkey);
        if (cachedType == null) {
            classTypeCache.putIfAbsent(argkey, argkey);
            cachedType = classTypeCache.get(argkey);
        }
        Object o = JSON.parseObject(bytes, cachedType);
    }
}
8

ParameterizedTypeImpl 这个你能自己缓存起来么?或者静态化做成员变量,或者使用TypeReference

确定这个bug已经修复? 我们使用1.2.47还有同样的问题,并且查看源码并没有解决。 另外我们的场景不能使用缓存Type来解决。

0

One instance of "com.alibaba.fastjson.parser.ParserConfig" loaded by "sun.misc.Launcher$AppClassLoader @ 0xa0000000" occupies 92,203,752 (34.28%) bytes. The memory is accumulated in one instance of "com.alibaba.fastjson.util.IdentityHashMap$Entry[]" loaded by "sun.misc.Launcher$AppClassLoader @ 0xa0000000".

Keywords com.alibaba.fastjson.util.IdentityHashMap$Entry[] com.alibaba.fastjson.parser.ParserConfig sun.misc.Launcher$AppClassLoader @ 0xa0000000 我用的1.2.56版本,还是有这个内存泄漏的问题,我困扰了三天,一直以为是自己使用不当造成的,希望能早点修复这个问题吧,最近压测服务,这个问题基本上百分百必现,真是个深坑,我们的请求响应泛型用的比较多,所以容易出现这个问题,但是用jackson就不会有这问题,有点小失落。。。

3

`private final static Type typeReference = new TypeReference<ESResultVO>() { }.getType();

static {
    ParserConfig.getGlobalInstance().getDeserializer(typeReference);
}

@Test
public void json() {
    ExecutorService executorService = ThreadUtil.newExecutor();
    while (true) {
        for (int j = 0; j < 10; j++) {
            executorService.submit(this::test);
        }
    }
}

private void test() {
    ESResultVO<TaskLogVO> json = JSON
        .parseObject("{\n" + "  \"code\": 0,\n" + "  \"message\": null,\n" + "  \"result\": {\n" + "    \"took\": 16,\n" + "    \"count\": 1,\n" + "    \"list\": [\n"
                     + "      {\n" + "        \"appId\": 4,\n" + "        \"bizNo\": \"\",\n" + "        \"elapsed\": 0,\n" + "        \"endTime\": 1567044743242,\n"
                     + "        \"gmtCreate\": 1567044743242,\n" + "        \"gmtUpdate\": 1567044743242,\n" + "        \"guid\": \"1fac95e9-081e-4e7a-965e-3279b306f1c3\",\n"
                     + "        \"id\": \"bc261686-11ef-461d-9ca0-a7aab09112b2\",\n" + "        \"startTime\": 1567044743242,\n" + "        \"status\": 0,\n"
                     + "        \"taskGuid\": \"3c704084-bc66-448d-86ff-f707f3d38317\",\n" + "        \"taskId\": 74,\n"
                     + "        \"traceId\": \"7b979533f94d48908d8fdd0d386a8400\",\n" + "        \"_score\": 0\n" + "      }\n" + "    ]\n" + "  }\n" + "}",
            typeReference);
}`

这么干可以解决这个问题,是个坑啊。最新的1.2.59,依然存在。有时候爆涨很快,有时候很慢,看运气。