fastjson反序列化使用不当导致内存泄露
分析一个线上内存告警的问题时,发现了造成内存告警的原因是使用fastjson不当导致的。
分析dump发现com.alibaba.fastjson.util.IdentityHashMap$Entry对象比较多。
查找相关文档
- fastjson IdentityHashMap 内存泄漏排查 (这篇文档分析描述的情况与我们遇到的问题的原因一样,是使用com.alibaba.fastjson.util.ParameterizedTypeImpl不当导致的)
- fastjon官方在很早的版本就修复过类似的问题,https://github.com/alibaba/fastjson/issues/849 ,相关代码:https://github.com/alibaba/fastjson/commit/ef50a5b756a6cab1ab753f4a661bdfb0ccbd6b7e ,他们修复的这个bug是针对com.alibaba.fastjson.TypeReference,这个类实际也是基于com.alibaba.fastjson.util.ParameterizedTypeImpl的。
问题产生的原因分析
- com.alibaba.fastjson.ParserConfig定义一个字段用于缓存不同类的反序列化器,使用的是IdentityHashMap(IdentityHashMap使用的是==比较key的值,不同于HashMap使用equals比较),缓存是以Type为key:
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
fastjson反序列化使用不当导致内存泄露的更多相关文章
- Andorid 内存溢出与内存泄露,几种常见导致内存泄露的写法
内存泄露,大部分是因为程序的逻辑不严谨,但是又可以跑通顺,然后导致的,内存溢出不会报错,如果不看日志信息是并不知道有泄露的.但是如果一直泄露,然后最终导致的内存溢出,仍然会使程序挂掉.内存溢出大部分是 ...
- MPMoviePlayerController导致statusBar消失,导致内存泄露leak
1.MPMoviePlayerController使statusBar消失 同事写项目时,运行程序总导致statusBar状态条消失,然后就是界面会上移20个像素,导致最下面空白界面,找原因一直不知道 ...
- ThreadLocal是否会导致内存泄露
什么是内存泄露? 维基百科的定义:[内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存],我的理解就是程序失去了对某段内存的控制,那么这段内存就算是泄露了. ThreadLocal为什么会导致 ...
- Android开发 静态static类与static方法持有Context是否导致内存泄露的疑问
简述 在Android开发的过程中,难免会使用单例模式或者静态方法工具类.我们会让它们持有一些外部的Context或者View一般有以下几种情况: 单例模式,类的全局变量持有Context 或 Vie ...
- 一个很初级的错误 Destructor忘记override导致内存泄露
TxxObj= class public Destructor Destroy(); override;!!!此处若无override,将导致内存泄露 end; Destru ...
- [C++11]shared_ptr循环引用导致内存泄露
1 /* 2 * shared_ptr循环引用导致内存泄露 3 */ 4 5 struct A 6 { 7 shared_ptr<A> ptr; // 改为weak_ptr<A> ...
- ThreadLocal源码分析以及why导致内存泄露
1 ThreadLocal? This class provides thread-local variables. These variables differ from their normal ...
- Handler导致内存泄露分析
(非静态)内部类引起内存泄漏的原因 内部类的实现其实是通过编译器的语法糖(Syntactic sugar)实现的,通过生成相应的子类即以OutClassName$InteriorCla ...
- BingMap频繁Add Pushpin和Delete Pushpin会导致内存泄露
近期在做性能測试的时候发现BingMap内存泄露(memory leak)的问题,查找了一些国外的帖子,发现也有类似的问题,可是没有好的解决的方法. https://social.msdn.micro ...
随机推荐
- Vue中插槽slot的使用
插槽,也就是slot,是组件的一块HTML模板,这块模板显示不显示.以及怎样显示由父组件来决定. 实际上,一个slot最核心的两个问题在这里就点出来了,是显示不显示和怎样显示. 由于插槽是一块模板,所 ...
- Hyperspectral Images Classification Based on Dense Convolutional Networks with Spectral-Wise Attention Mechanism
借鉴了DenseNet的思想,用了空洞卷积而不是池化,使得特征图不会缩小,因此每个dense连接都可以直接连,最后一层是包括了前面所有层的特征图. 此外还加入了channel-wise的注意力,对每个 ...
- 四种CSS样式的引入方式
准备 1.首先准备一个html文件:test.html,不建议使用记事本创建文件,建议使用Notepad++来创建并编辑文件,注意编码格式为:以UTF-8无BOM格式编码,否则会出现中文乱码,内容如下 ...
- mysql 选出前五个元素
mysql> select * from test; +----+----------+-------+-----------+ | id | name | score | subject | ...
- NIO Channel Socket套接字相关Channel
阻塞非阻塞: NIO中的Channel主要分为两大类:一类是FileChannel,另一类是SocketChannel.NIO提供的核心非阻塞特性主要针对SocketChannel类,全部socket ...
- HDFS练习
利用Shell命令与HDFS进行交互 以”./bin/dfs dfs”开头的Shell命令方式 1.目录操作 在HDFS中为hadoop用户创建一个用户目录(hadoop用户) 在用户目录下创建一个i ...
- 单细胞数据整合方法 | Comprehensive Integration of Single-Cell Data
操作代码:https://satijalab.org/seurat/ 依赖的算法 CCA CANONICAL CORRELATION ANALYSIS | R DATA ANALYSIS EXAMPL ...
- win10回收站右键有2个“CCleaner”怎么删除
win10回收站右键有2个"CCleaner"怎么删除? win10系统安装最新的CCleaner后遇到了这样子的问题:右击回收站有两个关于CCleaner的乱码,卸载CCle ...
- CMU Database Systems - Distributed OLTP & OLAP
OLTP scale-up和scale-out scale-up会有上限,无法不断up,而且相对而言,up升级会比较麻烦,所以大数据,云计算需要scale-out scale-out,就是分布式数据库 ...
- MXNet 定义新激活函数(Custom new activation function)
https://blog.csdn.net/weixin_34260991/article/details/87106463 这里使用比较简单的定义方式,只是在原有的激活函数调用中加入. 准备工作下载 ...