内存泄露 排查 多线程 hashmap

  • 线上一个模块内存泄露了。通过一系列命令查看,有以下特征:
    • 并发时才会复现
    • 老年代居高不下
    • CPU占用一直往上飙升
  • 复现办法
    • @Test
      public void testStream() {
          Long size = 1000000l;
          Map<String, String> map = new HashMap<>();
          List<String> list = new ArrayList<>();
          for (long i = 0; i < size; i++) {
              list.add(UUID.randomUUID().toString());
          }
          list.parallelStream().forEach(node -> map.put(node, node));
      }

       

  • 使用工具
    • jmap -histo:live pid 可以看到不同的类的占用的空间大小
    • jstack 可以拿到当前栈的快照
      • 使用例子
        • 使用 jstack 命令, 将java进程所有的线程堆栈信息输出到文件:  jstack 14739 > stack.log
        • 使用命令统计各线程情况:
        • cat stack.log | grep "\.java" | grep explink | sort | uniq -c
        • 输出结果:
        • 12837         at cn.explink.b2c.weisuda.threadpool.SubExcuteWeisudaTask.run(SubExcuteWeisudaTask.java:76)
        • 表示有12837个线程是在执行 SubExcuteWeisudaTask.java 的 76 行代码。
    • jstat -gcutil pid interval(ms) gc查看
    • 使用命令,查看 JAVA 进程创建了多少线程:    ps -Te | grep java  | wc
    • jmap -dump:format=b,file=/tmp/dump 38648 dump内存信息然后分析:
  • 分析过程
    • 首先看有没有静态变量一直引用着并且不断膨胀:没有
    • 看看并发的情况下,有没有哪些地方会出问题:
      • jstack发现stream里用的parallelStream一直在运行:经过看一些文章,纯粹用parallelStream应该是没有问题的,问题可能出在parallelStream+hashmap.put
      • 再看看jstack,多个线程都是在运行hashmap put里。resize()的时候卡死了【是多个线程都这样吗?】
        • 如果真的是多个线程卡死在这,猜测可能的原因:
          • 多个线程对同一个hashmap进行插入。(具体为什么会发生问题其实不好确定。但是可以肯定的是:多线程,没有锁,都去写,肯定有问题,其实分析到这可以确定这肯定有问题,但是不一定是这个问题导致的内存泄露,可以先修这个问题了)
          • 下面猜想一下可能发生的情况
            • 多个线程都发现hashmap达到一定阈值的时候需要resize。java.util.HashMap#resize
            • A线程先申请新的区域,全局table设置成A申请的新区域,进行复制。
            • B线程也申请了新的区域,这时候拿到的全局table是A申请的新区域,还在复制中呢,根据这个内容复制出来的不是完整的。可是这也不会爆内存啊。【此路不通】
      • 还是想多线程同时put的问题,需要等到每一个都put完最终才能执行完这段代码
        • 有很多个put的时候发生了resize。
    • 【这个很接近了真相了】多线程同时写hashmap发生不可预知的后果:https://stackoverflow.com/questions/40238099/is-it-safe-to-use-parallelstream-to-populate-a-map-in-java-8
    • 复现:利用上面的代码,跑起来,发现停不下来。用idea debug,卡死的时候按暂停,发现一直在一个找根的代码里循环出不来。所以就是树成环了。java.util.HashMap.TreeNode#root
  • 问题解决
    • 最终就是通过将parallelStream换成stream解决了问题
  • 参考
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值