[alibaba/fastjson][BUG]多线程调用JSON.parseObject出现内存泄漏

2024-08-28 408 views
5

我们的已有代码中存在多线程调用反序列化方法:

JSON.parseObject(str, xxx);

的场景。压测的时候,发现堆中出现大量的DefaultFieldDeserializer对象,导致内存占用飙升并最终OOM。

改成如下的写法后内存泄漏的BUG消失:

JSONObject jo = JSONObject.parseObject(str);
jo.toJavaObject(xxx);

我们基于1.2.59最新版本进行测试,发现上述BUG仍然存在。

回答

3

我也遇到了类似的问题,公司在对项目进行压测,fastjson反序列化泛型对象时,产生了大量com.alibaba.fastjson.util.FieldInfo对象,我猜测这个是用来描述对象的字段类型的,由于是解析请求和响应对象,数量多,而这些对象又无法释放,导致服务内存飙升,最后应用内存被吃光

8

@danyuandefine 你好,这个BUG肯定存在的,我们在1.2.59上面压测还是能够复现的,我们目前已经解决了,通过替换成JSONObject进行反序列化可以避免该问题。我这几天没有来关注,刚刚把标题改了一下,这样描述应该是更准确。网上有一个FastJSON内存泄漏的帖子比较相关,https://www.cnblogs.com/yooung/p/9563445.html,但是这个帖子里面的版本应该是比较早了,很多代码已经对不上了,不过我看了一下,现在还是有动态生成类的逻辑,不知道是不是这个地方造成的。总之这里确实是一个坑,没有修复之前还是注意。我们最近使用阿里的Druid连接池的时候,发现Druid的stat监控也存在内存泄漏的问题,必须把它的监控关了才能安全,OMG!

4

`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,依然存在。有时候爆涨很快,有时候很慢,看运气。

5

@tonycody 你的答复代码显示有点问题,建议调整一下,这FastJSON没人维护了吗,怎么提了这么明确的BUG都没有开发者答复。

6

最后发现问题了,TypeReference 如果放进去的是泛型对象,必须要这样写就可以了。亲试,没毛病了。只是很恶心,不好好看源码,根本不知道。因为不加Entity.class也能使用。但会造成内存缓慢增长... new TypeReference<User<Entity>>(Entity.class) {}

0

@darren-wang 找到组织了,我看了你推荐的博客,也多次测试,反复看了fastjson的源码,发现fastjson的会对对象类型做描述,并缓存在一个自定义的IdentityHashMap的数据结构中,该map为了最大限度的提升性能,采用了完全无锁化的方式存取对象类型,在解析大量泛型类时,会出现严重的内存泄漏问题,由于时间原因,项目要上线,我换回了jackson进行请求响应的解析,该问题便不存在了,有空再详细分析一下,感觉不是那么容易解决,毕竟性能和存储空间不可兼得,要改,估计得好好调整下类型描述信息的缓存机制了,这里是我分析该问题的方法随笔,希望帮到遇到类似问题的同仁们,地址:JVM内存泄漏排查方法随笔