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


    最近使用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());
        }
    
    }
  • 相关阅读:
    【原】csv文件导入MySQL数据库的实践
    【原】两个时间相加的运算符重载实现
    【转】趣味题:"Hello,world"的输出
    【转】WordPress源码解读(3)
    轻松记住大端小端的含义(附对大端和小端的解释)
    《编程精粹》书摘与读书笔记
    malloc/free函数的简单实现及思考
    如何写出正确的二分查找?——利用循环不变式理解二分查找及其变体的正确性以及构造方式
    Essential C++学习笔记备忘
    Linux中随手可得的测试、调试、性能检验工具
  • 原文地址:https://www.cnblogs.com/549294286/p/7784371.html
Copyright © 2020-2023  润新知