誤用WeakHashMap引發的死循環cpu跑滿問題

最近使用mvel 2.2.0.Final,出現一次cpu跑滿,通過線程棧分析,發現是誤用WeakHashMap引發的。java

故障現場:git

看WeakHashMap源碼:github

    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,修復詳情併發

問題復現:app

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

}
相關文章
相關標籤/搜索