V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
manecocomph
V2EX  ›  Java

如何确诊由 HashMap 引起的 死循环问题

  •  
  •   manecocomph · 2019-06-26 11:16:25 +08:00 · 3980 次点击
    这是一个创建于 1956 天前的主题,其中的信息可能已经有所发展或是发生改变。

    Java 开发者都知道 java.util.HashMap 是非线程安全的, 一旦涉及多线程情况使用同一个 Map, 除非做了其他同步, 否则都不应该使用 HashMap. 然而现实情况是: 时不时发现生产环境在多线程情况下使用了 HashMap, 导致死循环, 继而不再响应其他请求.

    那么如何诊断并确认是这种情况呢?

    首先, 我们从症状看起, 一般出现了这种情况, 1. CPU 使用率相对平时情况会高很多; 2. tomcat http 线程陷入 HashMap 死循环, 无法退出, 导致不能响应新的请求;

    一旦发生上面症状, 就可以怀疑 HashMap 导致的死循环问题了. 要想确认是不是这个问题, 基本需要 2 步: 1. 查看 thread dump, 看是不是有线程一直在 HashMap.getEntry() 上面; 2. 查看 heap dump 里面是不是 HashMap 某个桶的链表出现了死循环.

    1. 查看 thread dump, 确认是不是有线程一直在 HashMap.getEntry() 上面

      使用 jstack 做 thread dump, 查看是不是有一个或多个线程最顶上的栈像下面这样:

      上面的截图可以看到, 我这个 thread dump 里面有 40 个线程都卡在这一行. 因为我的 tomcat 就开了 40 个 http daemon 线程, 基本每个都卡在这里了.

    2. 查看 heap dump, 确认链表死循环

      首先, 使用 jcmd 或 jmap 或其它工具做一个 heap dump; 然后使用 MAT 或者其他 heap 分析工具找出 HashMap 里某个 bucket 里面的循环链表. 我这里以 MAT 举例, 截图来说明.

      如上图, 找到这个线程, 可以看到栈里面和我们 thread dump 里面的基本一样, 它卡在那个 getEntry() 方法还没出来. 点开它的 local variable, 能看到当前正在循环的 HashMap$Entry. 然后右键点击这个变量, 通过菜单 List Objects -> with outgoing references, 可以看到当前 Entry refer 的 next 对象:

      从上面的图可以看到, 这 2 个 Entry 对象的 next 互相指向对方, 导致了死循环, 致使当前线程陷入之后, 无法退出.

    通过上面的 2 步, 基本就确诊了是不是由 HashMap 引起的死循环问题.

    原文链接: http://www.tianxiaohui.com/index.php/interestingbug.html

    16 条回复    2019-06-27 19:55:20 +08:00
    pifuant
        1
    pifuant  
       2019-06-26 12:54:55 +08:00
    诊断啥子, 当线程安全去用, 存在的问题没有几千也有几百,,,
    manecocomph
        2
    manecocomph  
    OP
       2019-06-26 14:27:26 +08:00
    @pifuant "存在的问题没有几千也有几百" 是指?
    qiyuey
        3
    qiyuey  
       2019-06-26 14:27:38 +08:00
    建议写一些高质量的文章,这个是在太基础了
    BBCCBB
        4
    BBCCBB  
       2019-06-26 14:34:47 +08:00
    jdk1.8 之前的 hashmap 多线程下有可能引起死循环,这是公开的秘密了。。虽然它不是一个线程安全的容器。
    wysnylc
        5
    wysnylc  
       2019-06-26 14:42:53 +08:00
    hashMap 线程不安全是基础知识
    解决的方案要么是 redis 要么 ThreadLocal,单机业务下的 ConcurrentHashMap 在分布式下没卵用
    wysnylc
        6
    wysnylc  
       2019-06-26 14:46:10 +08:00
    还有,记得跟进最新的 jdk 进行讲解
    JDK1.8 后,除了对 hashmap 增加红黑树结果外,对原有造成死锁的关键原因点(新 table 复制在头端添加元素)改进为依次在末端添加新的元素。虽然 JDK1.8 后添加红黑树改进了链表过长查询遍历慢问题和 resize 时出现导致 put 死循环的 bug,但还是非线性安全的,比如数据丢失等等。因此多线程情况下还是建议使用 concurrenthashmap。
    sagaxu
        7
    sagaxu  
       2019-06-26 14:51:34 +08:00 via Android
    没有明确指出线程安全的 API,那就是不安全的,不用管他实际上安不安全
    broadliyn
        8
    broadliyn  
       2019-06-26 14:51:44 +08:00
    楼主的博客要是都是这样质量的文章,还是早点关站吧。
    misaka19000
        9
    misaka19000  
       2019-06-26 14:55:49 +08:00
    楼上这些嘲讽的回复是怎么回事?楼主的重点显然是在如何定位死循环,而不是 HashMap 是否线程安全
    doomty
        10
    doomty  
       2019-06-26 18:22:21 +08:00
    一个定位问题的方法案例,可以的
    luozic
        11
    luozic  
       2019-06-26 18:27:32 +08:00 via iPhone
    分布式里面 hashmap 不是最常的问题
    manecocomph
        12
    manecocomph  
    OP
       2019-06-26 18:58:40 +08:00
    @qiyuey 好的, 尽快写
    manecocomph
        13
    manecocomph  
    OP
       2019-06-26 18:59:22 +08:00
    @BBCCBB 嗯, 这里关注如何确诊
    manecocomph
        14
    manecocomph  
    OP
       2019-06-26 19:00:14 +08:00
    @broadliyn 好的, 尽快关
    manecocomph
        15
    manecocomph  
    OP
       2019-06-26 19:01:36 +08:00
    @misaka19000 谢谢理解, 网上这种氛围很自然...
    zhady009
        16
    zhady009  
       2019-06-27 19:55:20 +08:00
    @misaka19000 同不能理解..
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1093 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 26ms · UTC 19:35 · PVG 03:35 · LAX 12:35 · JFK 15:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.