当前位置:   article > 正文

记录一次因HashMap死循环导致的线上cpu飚高问题_hashmap占用内存过大,怎么排查问题

hashmap占用内存过大,怎么排查问题

1、线上问题

线上有一个服务,起转发作用,也就是用户所有的请求都必须经过这个服务。有一次突然cpu飚高,看了下内存,也在逐步升高,且内存不会下降,导致最后内存与cpu双双告警。第一次出现时为了尽快解决问题,只是重启了下线上服务,后面就又正常运行了几个月,直到又出现了第二次

2、排查过程

2.1 线上cpu占用

线上cpu占用情况

2.2 打印线程日志

八股文里面经常教我们线上cpu飚高都是top看下占用的进程,再打印线程日志,看下占用高的线程栈日志。于是让运维查看对应pod的线程占用情况
在这里插入图片描述
这里可以看到,线程115,116,777他们占用都是差不多的,因此猜测肯定是代码问题,不同线程都用到了这个代码。
于是打开线程日志,pid为116的线程,对应16进制是0x74,全局搜索后也就是如下日志:
在这里插入图片描述
从日志中可以看到具体的代码。
于是打开代码一看,发现是代码中用到了一个静态的HashMap作为全局缓存

    private static Map<Long, AccountAPIKeyVO> _TOKEN_MAP = new HashMap<>(1000, 1000);
  • 1

这里面根据用户Id缓存用户的一个Key值。我们都知道HashMap是线程不安全的,首先全局缓存肯定不能用HashMap,而应该用ConcurrentHashMap

2.2 令人疑惑的问题

但是让我疑惑的是,就算HashMap有线程不安全的问题,为什么会导致cpu飚高呢?

八股文常说,HashMap在多线程使用主要有两个问题
jdk1.7:

  • 线程不安全,也就是一个线程写的数据可能会丢失
  • 头插法导致扩容时可能会导致循环链表
    jdk1.8使用尾插法,已经避免了循环链表问题
    但是我现在使用的是jdk1.8,为什么还会有死循环问题呢?

2.3 死循环原因:

于是查看HashMap源码,查看死循环的具体代码:
然后点进HashMap原发报错的那一行,发现是调用HashMap的get(String key)方法时,查找红黑树的节点方法时,导致了死循环
在这里插入图片描述
并且通过网上查找资料,最后得出一个重要结论:
jdk1.8虽然解决了扩容时的循环链表问题,但是因为本身就是一个线程不安全的类,没有任何加锁操作,其红黑树在节点旋转修改时,难以避免出现节点赋值错乱问题,从何形成死循环子午树!

最终结论

HasmMap本身不是线程安全的容器,在多线程情况下使用肯定会有各种问题。并且HashMap没有过期时间且大小会扩容,因此正好也印证了线上内存也在逐步升高的问题。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/573415
推荐阅读
相关标签
  

闽ICP备14008679号