赞
踩
线上有一个服务,起转发作用,也就是用户所有的请求都必须经过这个服务。有一次突然cpu飚高,看了下内存,也在逐步升高,且内存不会下降,导致最后内存与cpu双双告警。第一次出现时为了尽快解决问题,只是重启了下线上服务,后面就又正常运行了几个月,直到又出现了第二次
八股文里面经常教我们线上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。
但是让我疑惑的是,就算HashMap有线程不安全的问题,为什么会导致cpu飚高呢?
八股文常说,HashMap在多线程使用主要有两个问题
jdk1.7:
于是查看HashMap源码,查看死循环的具体代码:
然后点进HashMap原发报错的那一行,发现是调用HashMap的get(String key)方法时,查找红黑树的节点方法时,导致了死循环
并且通过网上查找资料,最后得出一个重要结论:
jdk1.8虽然解决了扩容时的循环链表问题,但是因为本身就是一个线程不安全的类,没有任何加锁操作,其红黑树在节点旋转修改时,难以避免出现节点赋值错乱问题,从何形成死循环子午树!
HasmMap本身不是线程安全的容器,在多线程情况下使用肯定会有各种问题。并且HashMap没有过期时间且大小会扩容,因此正好也印证了线上内存也在逐步升高的问题。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。