代码改变世界

误用WeakHashMap引起的死循环cpu跑满问题

2017-11-04 19:49  Loull  阅读(2256)  评论(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());
    }

}