fastjson反序列化使⽤不当导致内存泄露
分析⼀个线上内存告警的问题时,发现了造成内存告警的原因是使⽤fastjson不当导致的。
分析dump发现com.alibaba.fastjson.util.IdentityHashMap$Entry对象⽐较多。
查相关⽂档
1. (这篇⽂档分析描述的情况与我们遇到的问题的原因⼀样,是使⽤com.alibaba.fastjson.util.ParameterizedTypeImpl不当导致的)
2. fastjon官⽅在很早的版本就修复过类似的问题,,相关代码:,他们修复的这个bug是针对com.alibaba.fastjson.TypeReference,这
个类实际也是基于com.alibaba.fastjson.util.ParameterizedTypeImpl的。
问题产⽣的原因分析
1. com.alibaba.fastjson.ParserConfig定义⼀个字段⽤于缓存不同类的反序列化器,使⽤的是IdentityHashMap(IdentityHashMap使⽤的
是==⽐较key的值,不同于HashMap使⽤equals⽐较),缓存是以Type为key:
private final IdentityHashMap<Type, ObjectDeserializer> deserializers = new IdentityHashMap<Type, ObjectDeserializer>();
2. ⽽我们的业务代码是在调⽤⼀个接⼝后将结果反序列化,然后每次都去创建⼀个ParameterizedTypeImpl实例,⽽fastjson针对每次创
建的PamrameterizedTypeImpl都会作为⼀个key加⼊到deserizers中进⾏缓存。
// ... ...
ParameterizedTypeImpl type = new ParameterizedTYpeImpl(new Type[]{ SomeInfo.class }, null, CommonVO.class);
CommonVO<SomeInfo> result = (CommonVO<SomeInfo>)JSON.parseObject(jsonString, type);
所以,随着不断的请求发起,内存泄漏产⽣了。(上⾯提到的fastjson⾃⾝的bug修复就是针对不同的类型⼜采⽤了
ConcurrentHashMap基于Class进⾏了⼀次缓存)
问题修复
⽅法⼀:
由于这⾥主要只是因为泛型才⽤了ParameterizedTypeImp,并且只有这⼀处,所以可以简单粗暴把这个定义为局部变量的type改为private static final的全局变量就可以避免内存泄漏了
private static final ParameterizedTypeImpl SOME_INFO_TYPE = ...
⽅法⼆:
使⽤com.alibaba.fastjson.TypeReference。
JSON.parseObject(json, new TypeReference<CommonVO<T>>(SomeInfo.class) {});
问题模拟重现
代码:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.util.ParameterizedTypeImpl;
import flect.Type;
import java.util.Objects;
import urrent.atomic.AtomicLong;
public class LeakDemo {
public static void main(String[] args) {
/*
-Xms30m
-Xmx30m
-XX:+PrintGCDateStamps
-XX:+PrintGCDetails
-XX:+PrintHeapAtGC
-
XX:+PrintGCApplicationStoppedTime
-Xloggc:/tmp/gc_%p_%t_.log
-XX:+HeapDumpOnOutOfMemoryError
-XX:HeapDumpPath=/tmp/
fastjson怎么用*/
final long start = System.currentTimeMillis();
final AtomicLong counter = new AtomicLong(0);
@Override
public void run() {
System.out.println("count: " + ());
System.out.println("took " + (System.currentTimeMillis() - start) + " ms");
}
}));
SomeInfo someInfo = new SomeInfo();
someInfo.setName("Tom");
CommonVO<SomeInfo> result = new CommonVO<>();
result.setData(someInfo);
result.setRetCode(0);
result.setMessage("Success");
String json = JSONString(result);
// 模拟业务中不断的接⼝请求处理
while (true) {
ParameterizedTypeImpl type = new ParameterizedTypeImpl(new Type[]{SomeInfo.class}, null, CommonVO.class); CommonVO<SomeInfo> tmpResult = (CommonVO<SomeInfo>) JSON.parseObject(json, type);
counter.incrementAndGet();
}
}
public static class CommonVO<T> {
private int retCode;
private String message;
private T data;
public int getRetCode() {
return retCode;
}
public void setRetCode(int retCode) {
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
public static class SomeInfo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
执⾏结果:
java.lang.OutOfMemoryError: GC overhead limit exceeded
Dumping heap to /tmp/java_pid13092.hprof ...
Heap dump file created [48333772 bytes in 0.402 secs]
Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
at java.util.Bytes(ZipCoder.java:80)
at java.util.Entry(ZipFile.java:306)
at java.util.Entry(JarFile.java:227)
at java.util.JarEntry(JarFile.java:210)
at sun.misc.Resource(URLClassPath.java:840)
at sun.misc.URLClassPath$JarLoader.findResource(URLClassPath.java:818)
at sun.misc.(URLClassPath.java:226)
at sun.misc.URLClassPath$1.hasMoreElements(URLClassPath.java:236)
at java.URLClassLoader$3$1.run(URLClassLoader.java:583)
at java.URLClassLoader$3$1.run(URLClassLoader.java:581)
at java.security.AccessController.doPrivileged(Native Method)
at java.(URLClassLoader.java:580)
at java.URLClassLoader$3.hasMoreElements(URLClassLoader.java:605)
at sun.(CompoundEnumeration.java:45)
at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54)
at com.alibaba.fastjson.util.ServiceLoader.load(ServiceLoader.java:34)
at com.alibaba.fastjson.Deserializer(ParserConfig.java:468)
at com.alibaba.fastjson.Deserializer(ParserConfig.java:363)
at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:639) at com.alibaba.fastjson.JSON.parseObject(JSON.java:350)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:318)
at com.alibaba.fastjson.JSON.parseObject(JSON.java:281)
at LeakDemo.main(LeakDemo.java:45)
count: 17300
took 6332 ms
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论