记录一次因HashMap死循环导致的线上cpu飚高问题

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);

这里面根据用户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没有过期时间且大小会扩容,因此正好也印证了线上内存也在逐步升高的问题。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值