如何确诊由 HashMap 引起的 死循环问题 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
manecocomph
V2EX    Java

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

  •  
  •   manecocomph Jun 26, 2019 4602 views
    This topic created in 2497 days ago, the information mentioned may be changed or developed.

    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 replies    2019-06-27 19:55:20 +08:00
    pifuant
        1
    pifuant  
       Jun 26, 2019
    诊断啥子, 当线程安全去用, 存在的问题没有几千也有几百,,,
    manecocomph
        2
    manecocomph  
    OP
       Jun 26, 2019
    @pifuant "存在的问题没有几千也有几百" 是指?
    qiyuey
        3
    qiyuey  
       Jun 26, 2019
    建议写一些高质量的文章,这个是在太基础了
    BBCCBB
        4
    BBCCBB  
       Jun 26, 2019
    jdk1.8 之前的 hashmap 多线程下有可能引起死循环,这是公开的秘密了。。虽然它不是一个线程安全的容器。
    wysnylc
        5
    wysnylc  
       Jun 26, 2019
    hashMap 线程不安全是基础知识
    解决的方案要么是 redis 要么 ThreadLocal,单机业务下的 ConcurrentHashMap 在分布式下没卵用
    wysnylc
        6
    wysnylc  
       Jun 26, 2019
    还有,记得跟进最新的 jdk 进行讲解
    JDK1.8 后,除了对 hashmap 增加红黑树结果外,对原有造成死锁的关键原因点(新 table 复制在头端添加元素)改进为依次在末端添加新的元素。虽然 JDK1.8 后添加红黑树改进了链表过长查询遍历慢问题和 resize 时出现导致 put 死循环的 bug,但还是非线性安全的,比如数据丢失等等。因此多线程情况下还是建议使用 concurrenthashmap。
    sagaxu
        7
    sagaxu  
       Jun 26, 2019 via Android
    没有明确指出线程安全的 API,那就是不安全的,不用管他实际上安不安全
    broadliyn
        8
    broadliyn  
       Jun 26, 2019
    楼主的博客要是都是这样质量的文章,还是早点关站吧。
    misaka19000
        9
    misaka19000  
       Jun 26, 2019
    楼上这些嘲讽的回复是怎么回事?楼主的重点显然是在如何定位死循环,而不是 HashMap 是否线程安全
    doomty
        10
    doomty  
       Jun 26, 2019
    一个定位问题的方法案例,可以的
    luozic
        11
    luozic  
       Jun 26, 2019 via iPhone
    分布式里面 hashmap 不是最常的问题
    manecocomph
        12
    manecocomph  
    OP
       Jun 26, 2019
    @qiyuey 好的, 尽快写
    manecocomph
        13
    manecocomph  
    OP
       Jun 26, 2019
    @BBCCBB 嗯, 这里关注如何确诊
    manecocomph
        14
    manecocomph  
    OP
       Jun 26, 2019
    @broadliyn 好的, 尽快关
    manecocomph
        15
    manecocomph  
    OP
       Jun 26, 2019
    @misaka19000 谢谢理解, 网上这种氛围很自然...
    zhady009
        16
    zhady009  
       Jun 27, 2019
    @misaka19000 同不能理解..
    About     Help     Advertise     Blog     API     FAQ     Solana     4924 Online   Highest 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 78ms UTC 09:56 PVG 17:56 LAX 02:56 JFK 05:56
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86