分析一个线上内存告警的问题时,发现了造成内存告警的原因是使用fastjson不当导致的。
分析dump发现com.alibaba.fastjson.util.IdentityHashMap$Entry对象比较多。
private final IdentityHashMap<Type, ObjectDeserializer> deserializers = new IdentityHashMap<Type, ObjectDeserializer>();
而我们的业务代码是在调用一个接口后将结果反序列化,然后每次都去创建一个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) {});
https://github.com/alibaba/fastjson/wiki/TypeReference
代码:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.util.ParameterizedTypeImpl;
import java.lang.reflect.Type;
import java.util.Objects;
import java.util.concurrent.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/
*/
final long start = System.currentTimeMillis();
final AtomicLong counter = new AtomicLong(0);
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
System.out.println("count: " + counter.get());
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 = JSON.toJSONString(result);
// 模拟业务中不断的接口请求处理
while (true) {
ParameterizedTypeImpl type = new ParameterizedTypeImpl(new Type[]{SomeInfo.class}, null, CommonVO.class);
CommonVO<SomeInfo> tmpResult = (CommonVO<SomeInfo>) JSON.parseObject(json, type);
Objects.requireNonNull(tmpResult);
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) {
this.retCode = retCode;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = 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.zip.ZipCoder.getBytes(ZipCoder.java:80)
at java.util.zip.ZipFile.getEntry(ZipFile.java:306)
at java.util.jar.JarFile.getEntry(JarFile.java:227)
at java.util.jar.JarFile.getJarEntry(JarFile.java:210)
at sun.misc.URLClassPath$JarLoader.getResource(URLClassPath.java:840)
at sun.misc.URLClassPath$JarLoader.findResource(URLClassPath.java:818)
at sun.misc.URLClassPath$1.next(URLClassPath.java:226)
at sun.misc.URLClassPath$1.hasMoreElements(URLClassPath.java:236)
at java.net.URLClassLoader$3$1.run(URLClassLoader.java:583)
at java.net.URLClassLoader$3$1.run(URLClassLoader.java:581)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader$3.next(URLClassLoader.java:580)
at java.net.URLClassLoader$3.hasMoreElements(URLClassLoader.java:605)
at sun.misc.CompoundEnumeration.next(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.parser.ParserConfig.getDeserializer(ParserConfig.java:468)
at com.alibaba.fastjson.parser.ParserConfig.getDeserializer(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
Original url: Access
Created at: 2019-12-10 19:55:23
Category: default
Tags: none
未标明原创文章均为采集,版权归作者所有,转载无需和我联系,请注明原出处,南摩阿彌陀佛,知识,不只知道,要得到
最新评论