分析一个线上内存告警的问题时,发现了形成内存告警的缘由是使用fastjson不当致使的。
分析dump发现com.alibaba.fastjson.util.IdentityHashMap$Entry对象比较多。html
private final IdentityHashMap<Type, ObjectDeserializer> deserializers = new IdentityHashMap<Type, ObjectDeserializer>();
而咱们的业务代码是在调用一个接口后将结果反序列化,而后每次都去建立一个ParameterizedTypeImpl实例,而fastjson针对每次建立的PamrameterizedTypeImpl都会做为一个key加入到deserizers中进行缓存。java
// ... ... ParameterizedTypeImpl type = new ParameterizedTYpeImpl(new Type[]{ SomeInfo.class }, null, CommonVO.class); CommonVO<SomeInfo> result = (CommonVO<SomeInfo>)JSON.parseObject(jsonString, type);
因此,随着不断的请求发起,内存泄漏产生了。(上面提到的fastjson自身的bug修复就是针对不一样的类型又采用了ConcurrentHashMap基于Class进行了一次缓存)git
方法一:
因为这里主要只是由于泛型才用了ParameterizedTypeImp,而且只有这一处,因此能够简单粗暴把这个定义为局部变量的type改成private static final的全局变量就能够避免内存泄漏了github
private static final ParameterizedTypeImpl SOME_INFO_TYPE = ...
方法二:
使用com.alibaba.fastjson.TypeReference。json
JSON.parseObject(json, new TypeReference<CommonVO<T>>(SomeInfo.class) {});
https://github.com/alibaba/fastjson/wiki/TypeReference缓存
代码:hexo
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; } } }
执行结果:ide
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