误用WeakHashMap引起的死循环cpu跑满问题
2017-11-04 19:49 Loull 阅读(2244) 评论(0) 编辑 收藏 举报最近使用mvel 2.2.0.Final,出现一次cpu跑满,经过线程栈分析,发现是误用WeakHashMap引起的。
故障现场:
看WeakHashMap源码:
public V get(Object key) { Object k = maskNull(key); int h = hash(k); Entry<K,V>[] tab = getTable(); int index = indexFor(h, tab.length); Entry<K,V> e = tab[index]; while (e != null) { if (e.hash == h && eq(k, e.get())) return e.value; e = e.next; } return null; }
线程在WeakHashMap的get方法里面出不来了,一直在while循环里面。
多线程并发get和put,fullgc或gc的时候可能会出现。
因为gc会把对象给清理掉,然后get方法内的while循环一直找不到eq的对象,循环出不来。
WeakHashMap类已经说明了这个类不是线程安全的。在[2.1.8.Final,~]以上修复了,除了2.2.0.Final,修复详情。
问题复现:
import java.util.Map; import java.util.Random; import java.util.WeakHashMap; public class WeakHashMapTest { Thread thread = new Thread(new Runnable() { @Override public void run() { } }); public static void main(String[] args) throws InterruptedException { Random random = new Random(); // Map<String, String> weak = Collections.synchronizedMap(new WeakHashMap<>());//OK Map<String, String> weak = new WeakHashMap<>(); for (int i = 0; i < 10; i++) { weak.put(new String("" + i), "" + i); } for (int i = 0; i < 20; i++) { new Thread(new Runnable() { @Override public void run() { StringBuffer sb = new StringBuffer(); for (int k = 0; k < 200; k++) { sb.append(weak.get(new String("" + (k) % 10))); if (k % 17 == 0) { System.gc(); } int nextInt = random.nextInt(10); weak.put(new String("" + nextInt), "" + nextInt); } System.out.println("end:" + sb.toString()); } }).start(); } System.gc(); System.out.println("sleep"); Thread.sleep(10000); System.out.println("exit"); System.out.println("exit2"); } static void test1() { Map<String, String> weak = new WeakHashMap<>(); weak.put(new String("1"), "1"); weak.put(new String("2"), "2"); weak.put(new String("3"), "3"); weak.put(new String("4"), "4"); weak.put(new String("5"), "5"); weak.put(new String("6"), "6"); System.out.println(weak.size()); System.gc(); //手动触发 Full GC try { Thread.sleep(50); //我的测试中发现必须sleep一下才能看到不一样的结果 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(weak.size()); } }