1. 问题描述:
短信平台压测过程中遇到,频繁FullGC问题, CPU使用率达到90%,fullGC基本上5s中一次, 压测结束后一直持续。
2. 原因分析:
2.1. 获取进程的堆栈日志
jstack pid > jstack.pid.log
2.2. 利用gceasy网站进行堆栈分析
官网地址: gceasy.io/ 我们可以看到一共有419个线程,大部分处于wating状态, 有8个处于阻塞的状态。
堆栈分析
有8个线程基本上处于阻塞的状态,看下线程,主要mq是1mq的生产者线程和7个消费者线程处于阻塞状态,看下堆栈日志
基本上可以确认是CurrentHashmap的用法不当导致了死锁问题。
看下网站机器学习对问题原因的分析
2.4 根本原因分析
看下出现问题的代码, 上面的意思是,在计算过程中,其他线程尝试对Map执行的某些更新操作可能会被阻止,因此计算应简短而简单,并且不得尝试更新此映射的任何其他映射。
ConcurrentHashMap 源码分析
问题复现
public static void main(String[] args) {
Map<String, Integer> map = new ConcurrentHashMap<>(16);
System.out.println("AaAa".hashCode());
System.out.println("BBBB".hashCode());
map.computeIfAbsent(
"AaAa",
key -> {
return map.computeIfAbsent(
"BBBB",
key2 -> 42);
}
);
}
复制代码
执行上面的代码会导致死锁,当key="AaAa" 不存在的时候,插入key="BBBB"的数据。但是两个key的haseCode 值一样导致的死锁。
ConcurrentHashMap#computeIfAbsent 源码
public V compute(K key,
BiFunction<? super K, ? super V, ? extends V> remappingFunction) {
if (key == null || remappingFunction == null)
throw new NullPointerException();
int h = spread(key.hashCode());
V val = null;
int delta = 0;
int binCount = 0;
for (Node<K,V>[] tab = table;;) {
Node<K,V> f; int n, i, fh;
if (tab == null || (n = tab.length) == 0)
tab = initTable();
else if ((f = tabAt(tab, i = (n - 1) & h)) == null) {
Node<K,V> r = new ReservationNode<K,V>();
synchronized (r) {
if (casTabAt(tab, i, null, r)) {
binCount = 1;
Node<K,V> node = null;
try {
if ((val = remappingFunction.apply(key, null)) != null) {
delta = 1;
node = new Node<K,V>(h, key, val, null);
}
} finally {
setTabAt(tab, i, node);
}
}
}
if (binCount != 0)
break;
}
else if ((fh = f.hash) == MOVED)
tab = helpTransfer(tab, f);
else {
// 第1868行代码
synchronized (f) {
if (tabAt(tab, i) == f) {
if (fh >= 0) {
binCount = 1;
for (Node<K,V> e = f, pred = null;; ++binCount) {
K ek;
if (e.hash == h &&
((ek = e.key) == key ||
(ek != null && key.equals(ek)))) {
val = remappingFunction.apply(key, e.val);
if (val != null)
e.val = val;
else {
delta = -1;
Node<K,V> en = e.next;
if (pred != null)
pred.next = en;
else
setTabAt(tab, i, en);
}
break;
}
pred = e;
if ((e = e.next) == null) {
val = remappingFunction.apply(key, null);
if (val != null) {
delta = 1;
pred.next =
new Node<K,V>(h, key, val, null);
}
break;
}
}
}
else if (f instanceof TreeBin) {
binCount = 1;
TreeBin<K,V> t = (TreeBin<K,V>)f;
TreeNode<K,V> r, p;
if ((r = t.root) != null)
p = r.findTreeNode(h, key, null);
else
p = null;
V pv = (p == null) ? null : p.val;
val = remappingFunction.apply(key, pv);
if (val != null) {
if (p != null)
p.val = val;
else {
delta = 1;
t.putTreeVal(h, key, val);
}
}
else if (p != null) {
delta = -1;
if (t.removeTreeNode(p))
setTabAt(tab, i, untreeify(t.first));
}
}
}
}
if (binCount != 0) {
if (binCount >= TREEIFY_THRESHOLD)
treeifyBin(tab, i);
break;
}
}
}
if (delta != 0)
addCount((long)delta, binCount);
return val;
}
复制代码