分析一个线上内存告警的问题时,发现了造成内存告警的原因是使用fastjson不当导致的。

分析dump发现com.alibaba.fastjson.util.IdentityHashMap$Entry对象比较多。

查找相关文档

  1. fastjson IdentityHashMap 内存泄漏排查 (这篇文档分析描述的情况与我们遇到的问题的原因一样,是使用com.alibaba.fastjson.util.ParameterizedTypeImpl不当导致的)
  2. 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的。

问题产生的原因分析

  1. com.alibaba.fastjson.ParserConfig定义一个字段用于缓存不同类的反序列化器,使用的是IdentityHashMap(IdentityHashMap使用的是==比较key的值,不同于HashMap使用equals比较),缓存是以Type为key:
    1. private final IdentityHashMap<Type, ObjectDeserializer> deserializers = new IdentityHashMap<Type, ObjectDeserializer>();
  2. 而我们的业务代码是在调用一个接口后将结果反序列化,然后每次都去创建一个ParameterizedTypeImpl实例,而fastjson针对每次创建的PamrameterizedTypeImpl都会作为一个key加入到deserizers中进行缓存。
    1. // ... ...
    2. ParameterizedTypeImpl type = new ParameterizedTYpeImpl(new Type[]{ SomeInfo.class }, null, CommonVO.class);
    3. CommonVO<SomeInfo> result = (CommonVO<SomeInfo>)JSON.parseObject(jsonString, type);

    所以,随着不断的请求发起,内存泄漏产生了。(上面提到的fastjson自身的bug修复就是针对不同的类型又采用了ConcurrentHashMap基于Class进行了一次缓存)

问题修复

方法一:

由于这里主要只是因为泛型才用了ParameterizedTypeImp,并且只有这一处,所以可以简单粗暴把这个定义为局部变量的type改为private static final的全局变量就可以避免内存泄漏了

  1. private static final ParameterizedTypeImpl SOME_INFO_TYPE = ...

方法二:

使用com.alibaba.fastjson.TypeReference。

  1. JSON.parseObject(json, new TypeReference<CommonVO<T>>(SomeInfo.class) {});

https://github.com/alibaba/fastjson/wiki/TypeReference

问题模拟重现

代码:

  1. import com.alibaba.fastjson.JSON;
  2. import com.alibaba.fastjson.util.ParameterizedTypeImpl;
  3. import java.lang.reflect.Type;
  4. import java.util.Objects;
  5. import java.util.concurrent.atomic.AtomicLong;
  6. public class LeakDemo {
  7. public static void main(String[] args) {
  8. /*
  9. -Xms30m
  10. -Xmx30m
  11. -XX:+PrintGCDateStamps
  12. -XX:+PrintGCDetails
  13. -XX:+PrintHeapAtGC
  14. -XX:+PrintGCApplicationStoppedTime
  15. -Xloggc:/tmp/gc_%p_%t_.log
  16. -XX:+HeapDumpOnOutOfMemoryError
  17. -XX:HeapDumpPath=/tmp/
  18. */
  19. final long start = System.currentTimeMillis();
  20. final AtomicLong counter = new AtomicLong(0);
  21. Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
  22. @Override
  23. public void run() {
  24. System.out.println("count: " + counter.get());
  25. System.out.println("took " + (System.currentTimeMillis() - start) + " ms");
  26. }
  27. }));
  28. SomeInfo someInfo = new SomeInfo();
  29. someInfo.setName("Tom");
  30. CommonVO<SomeInfo> result = new CommonVO<>();
  31. result.setData(someInfo);
  32. result.setRetCode(0);
  33. result.setMessage("Success");
  34. String json = JSON.toJSONString(result);
  35. // 模拟业务中不断的接口请求处理
  36. while (true) {
  37. ParameterizedTypeImpl type = new ParameterizedTypeImpl(new Type[]{SomeInfo.class}, null, CommonVO.class);
  38. CommonVO<SomeInfo> tmpResult = (CommonVO<SomeInfo>) JSON.parseObject(json, type);
  39. Objects.requireNonNull(tmpResult);
  40. counter.incrementAndGet();
  41. }
  42. }
  43. public static class CommonVO<T> {
  44. private int retCode;
  45. private String message;
  46. private T data;
  47. public int getRetCode() {
  48. return retCode;
  49. }
  50. public void setRetCode(int retCode) {
  51. this.retCode = retCode;
  52. }
  53. public String getMessage() {
  54. return message;
  55. }
  56. public void setMessage(String message) {
  57. this.message = message;
  58. }
  59. public T getData() {
  60. return data;
  61. }
  62. public void setData(T data) {
  63. this.data = data;
  64. }
  65. }
  66. public static class SomeInfo {
  67. private String name;
  68. public String getName() {
  69. return name;
  70. }
  71. public void setName(String name) {
  72. this.name = name;
  73. }
  74. }
  75. }

执行结果:

  1. java.lang.OutOfMemoryError: GC overhead limit exceeded
  2. Dumping heap to /tmp/java_pid13092.hprof ...
  3. Heap dump file created [48333772 bytes in 0.402 secs]
  4. Exception in thread "main" java.lang.OutOfMemoryError: GC overhead limit exceeded
  5. at java.util.zip.ZipCoder.getBytes(ZipCoder.java:80)
  6. at java.util.zip.ZipFile.getEntry(ZipFile.java:306)
  7. at java.util.jar.JarFile.getEntry(JarFile.java:227)
  8. at java.util.jar.JarFile.getJarEntry(JarFile.java:210)
  9. at sun.misc.URLClassPath$JarLoader.getResource(URLClassPath.java:840)
  10. at sun.misc.URLClassPath$JarLoader.findResource(URLClassPath.java:818)
  11. at sun.misc.URLClassPath$1.next(URLClassPath.java:226)
  12. at sun.misc.URLClassPath$1.hasMoreElements(URLClassPath.java:236)
  13. at java.net.URLClassLoader$3$1.run(URLClassLoader.java:583)
  14. at java.net.URLClassLoader$3$1.run(URLClassLoader.java:581)
  15. at java.security.AccessController.doPrivileged(Native Method)
  16. at java.net.URLClassLoader$3.next(URLClassLoader.java:580)
  17. at java.net.URLClassLoader$3.hasMoreElements(URLClassLoader.java:605)
  18. at sun.misc.CompoundEnumeration.next(CompoundEnumeration.java:45)
  19. at sun.misc.CompoundEnumeration.hasMoreElements(CompoundEnumeration.java:54)
  20. at com.alibaba.fastjson.util.ServiceLoader.load(ServiceLoader.java:34)
  21. at com.alibaba.fastjson.parser.ParserConfig.getDeserializer(ParserConfig.java:468)
  22. at com.alibaba.fastjson.parser.ParserConfig.getDeserializer(ParserConfig.java:363)
  23. at com.alibaba.fastjson.parser.DefaultJSONParser.parseObject(DefaultJSONParser.java:639)
  24. at com.alibaba.fastjson.JSON.parseObject(JSON.java:350)
  25. at com.alibaba.fastjson.JSON.parseObject(JSON.java:318)
  26. at com.alibaba.fastjson.JSON.parseObject(JSON.java:281)
  27. at LeakDemo.main(LeakDemo.java:45)
  28. count: 17300
  29. took 6332 ms

fastjson反序列化使用不当导致内存泄露的更多相关文章

  1. Andorid 内存溢出与内存泄露,几种常见导致内存泄露的写法

    内存泄露,大部分是因为程序的逻辑不严谨,但是又可以跑通顺,然后导致的,内存溢出不会报错,如果不看日志信息是并不知道有泄露的.但是如果一直泄露,然后最终导致的内存溢出,仍然会使程序挂掉.内存溢出大部分是 ...

  2. MPMoviePlayerController导致statusBar消失,导致内存泄露leak

    1.MPMoviePlayerController使statusBar消失 同事写项目时,运行程序总导致statusBar状态条消失,然后就是界面会上移20个像素,导致最下面空白界面,找原因一直不知道 ...

  3. ThreadLocal是否会导致内存泄露

    什么是内存泄露? 维基百科的定义:[内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存],我的理解就是程序失去了对某段内存的控制,那么这段内存就算是泄露了. ThreadLocal为什么会导致 ...

  4. Android开发 静态static类与static方法持有Context是否导致内存泄露的疑问

    简述 在Android开发的过程中,难免会使用单例模式或者静态方法工具类.我们会让它们持有一些外部的Context或者View一般有以下几种情况: 单例模式,类的全局变量持有Context 或 Vie ...

  5. 一个很初级的错误 Destructor忘记override导致内存泄露

      TxxObj= class    public     Destructor Destroy(); override;!!!此处若无override,将导致内存泄露     end; Destru ...

  6. [C++11]shared_ptr循环引用导致内存泄露

    1 /* 2 * shared_ptr循环引用导致内存泄露 3 */ 4 5 struct A 6 { 7 shared_ptr<A> ptr; // 改为weak_ptr<A> ...

  7. ThreadLocal源码分析以及why导致内存泄露

    1 ThreadLocal? This class provides thread-local variables. These variables differ from their normal ...

  8. Handler导致内存泄露分析

    (非静态)内部类引起内存泄漏的原因         内部类的实现其实是通过编译器的语法糖(Syntactic sugar)实现的,通过生成相应的子类即以OutClassName$InteriorCla ...

  9. BingMap频繁Add Pushpin和Delete Pushpin会导致内存泄露

    近期在做性能測试的时候发现BingMap内存泄露(memory leak)的问题,查找了一些国外的帖子,发现也有类似的问题,可是没有好的解决的方法. https://social.msdn.micro ...

随机推荐

  1. 修改git 提交的用户名和用户Email命令

    首页先查看全局配置:git config --list git config --local --list 法一:使用命令修改git的用户名和提交的邮箱 )修改全局 如果你要修改当前全局的用户名和邮箱 ...

  2. vue-cli3 ios10白屏问题解决思路

    在出现了这个问题之后先不要盲目的去瞎试,根据网上的方法试了个遍也没解决问题 先看报的是什么错,再针对的解决问题 首先出现的报错是 SyntaxError: Unexpected token '*'  ...

  3. 洛谷 P1629 邮递员送信 题解

    P1629 邮递员送信 题目描述 有一个邮递员要送东西,邮局在节点1.他总共要送N-1样东西,其目的地分别是2~N.由于这个城市的交通比较繁忙,因此所有的道路都是单行的,共有M条道路,通过每条道路需要 ...

  4. CTS&&APIO2019爆零记

    如果你只好奇测试相关请跳至day 2 day 3 day 6 scoi 2019 之后 ​ 由于实力问题,省选的时候排名在三十多,显然是没有进队.不过可能是受过的打击比较多,所以还没有特别颓废,甚至连 ...

  5. Docker 安装ubuntu服务器

    ### 1. 安装ubuntu ```docker pull ubuntudocker run -it -d --name ubuntu_test -p 2222:22 ubuntu ``` ### ...

  6. GoCN每日新闻(2019-10-05)

     国庆专辑:GopherChina祝大家国庆节快乐GoCN每日新闻(2019-10-05) 1. Gophercon UK 2019 https://www.bilibili.com/video/av ...

  7. x64内核强删文件.

    目录 x64内核中强删文件的实现 一丶简介 1.步骤 2.Nt驱动代码 x64内核中强删文件的实现 一丶简介 说道删除文件.有各种各样的方法. 有ring3 也有ring0. 而且也有许多对抗的方法. ...

  8. 什么是TCP粘包?怎么解决这个问题

    在socket网络编程中,都是端到端通信,由客户端端口+服务端端口+客户端IP+服务端IP+传输协议组成的五元组可以明确的标识一条连接.在TCP的socket编程中,发送端和接收端都有成对的socke ...

  9. 网络营销CPA、CPS、CPM、CPT、CPC 是什么

    网络营销之所以越来越受到重视一个主要的原因就是因为“精准”.相比较传统媒体的陈旧广告形式,网络营销能为广告主带来更为确切的效果与回报,更有传统媒体所没有的即时互动性.很多企业借助于精准的网络营销成为人 ...

  10. 使用Nginx转发tcp请求

    要求nginx版本大于1.9,在nginx.conf添加以下,要求和http{}同级 stream { upstream cakehouse { server weight= max_fails= f ...