zoukankan      html  css  js  c++  java
  • WeakhashMap源码1

    弱引用(WeakReference)的特性是:当gc线程发现某个对象只有弱引用指向它,那么就会将其销毁并回收内存WeakReference也会被加入到引用队列queue中。 

    它的特殊之处在于 WeakHashMap 里的entry可能会被GC自动删除,即使程序员没有调用remove()或者clear()方法。

    可能发生如下情况:

    • 调用两次size()方法返回不同的值;
    • 两次调用isEmpty()方法,第一次返回false,第二次返回true
    • 两次调用containsKey()方法,第一次返回true,第二次返回false,尽管两次使用的是同一个key
    • 两次调用get()方法,第一次返回一个value,第二次返回null,尽管两次使用的是同一个对象。

    WeekHashMap 的这个特点特别适用于需要缓存的场景。

    GC判断某个对象是否可被回收的依据是,是否有有效的引用指向该对象。

    这里的“有效引用”并不包括弱引用。也就是说,虽然弱引用可以用来访问对象。

    将一对key, value放入到 WeakHashMap 里并不能避免该key对应的内存区域被GC回收。

    既然有 WeekHashMap,是否有 WeekHashSet 呢?答案是没有,不过Java Collections工具类给出了解决方案,

    // 将WeakHashMap包装成一个Set

    Set<Object> weakHashSet = Collections.newSetFromMap(new WeakHashMap<Object, Boolean>());

    如果存放在WeakHashMap中的key都存在强引用,那么WeakHashMap就会退化为HashMap

    使用WeakHashMap可以忽略容量问题,提升缓存容量。只是当容量不够时,不会OOM,内部数据会被GC回收。命中率好像没有办法,容我掉一片头发换来深度思考后给出方案。

    观察WeakHashMap源码可以发现,它是线程不安全的,所以在多线程场景该怎么办嘞?

    WeakHashMap<String, String> weakHashMapintsmaze=new WeakHashMap<String, String>();

    Map<String, String> intsmaze=Collections.synchronizedMap(weakHashMapintsmaze);

    public class Test11 {
        private static final int _1MB = 1024 * 1024;// 设置大小为1MB
        public static void main(String[] args) throws InterruptedException {
            Object value = new Object();
            WeakHashMap<Object, Object> map = new WeakHashMap<Object, Object>();
            for (int i = 0; i < 100; i++) {
                byte[] bytes = new byte[_1MB];
                // bytes和value构成Entry,bytes是弱引用,没有别人指向,就回去回收这个Entry。
                map.put(bytes, value);
            } // 其实当我们在循环100次添加数据时,就已经开始回收弱引用了,因此我们会看到第一次打印的size是13,而不是100,一旦GC发生,那么弱引用就会被清除,导致WeakHashMap的大小为0。
                //
            while (true) {
                System.gc();// 建议系统进行GC
                Thread.sleep(500);
                System.out.println(map.size());// 13 0 0 0 0
            }
        }
    }
    //WeakHashMap里面EntrySet的iterator()方法返回new EntryIterator(),
    //EntryIterator的next和hashNext方法是对WeakHashMap的table做的遍历。
    //所以new EntrySet(),就返回WeakHashMap的table的所有元素。
    
    public class eee {
        @SuppressWarnings("rawtypes")
        public static void main(String[] args) {
            Collection c = new eee().entrySet();//[6, 5, 4, 3, 2, 1]
            Iterator iter = c.iterator();
            while (iter.hasNext()) {
                String entry = (String) iter.next();
                System.out.println(entry);
            }
        }
        String[] table = new String[]{"1","2","3","4","5","6"};
        String[] table1 = new String[]{"11","21","31","41","51","61"};
        private transient Set<String> entrySet;
        
        public Set<String> entrySet() { 
            Set<String> es = entrySet; 
            return es != null ? es : (entrySet = new EntrySet());//是根据hasNext()和next()方法来确定集合元素的。
        }
        private class EntrySet extends AbstractSet<String>   { 
            public Iterator<String> iterator() {
                return new EntryIterator();    //return null 那么new EntrySet()就没有元素了。
            }
            @Override
            public int size() {
                return 0;
            }
        }
        private class EntryIterator<String>  implements Iterator<String>   { 
            private int index; 
            EntryIterator() {
                index =  table.length; 
    //            index =  table1.length; 
            }
            public boolean hasNext() {
                if (index > 0) return true;
                else return false;
            }
            public String next() {
                return (String) table[--index];
    //            return (String) table1[--index];
            }
        }
    }

    当一个键对象被垃圾回收,那么相应的值对象的引用会从Map中删除。WeakHashMap能够节约存储空间,可用来缓存那些非必须存在的数据。

    get,put,size,isEmpty,containsKey,getEntry,resize,拷贝,putAllremovecontainsValuecontainsNullValueforEach都会清理脏数据。

    keySetvaluesentrySet不会清理脏数据。

    WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。

    WeakHashMap的Key是弱引用,Value不是。WeakHashMap不会自动释放失效的弱引用tableEntry,仅当包含了expungeStaleEntries()的共有方法被调用的时候才会释放。

    WeakHashMap没有实现Clone和Serializable接口,所以不具有克隆和序列化的特性。

    WeakHashMap因为gc的时候会把没有强引用的key回收掉,所以注定了它里面的元素不会太多,因此也就不需要像HashMap那样元素多的时候转化为红黑树来处理了。

    WeakHashmap将会移除一些死的(dread)的entry,避免持有过多死的弱引用。

    ReferenceQuene能够轻易的追踪这些死掉的弱引用。可以讲ReferenceQuene传入WeakHashmap的构造方法(constructor)中,这样,一旦这个弱引用里面的对象成为垃圾,这个弱引用将加入ReferenceQuene中。

        public static void main(String args[]) {
            WeakHashMap<String, String> map = new WeakHashMap<String, String>();
            map.put(new String("1"), "1");
            map.put("2", "2");
            String s = new String("3");
            map.put(s, "3");
            while (map.size() > 0) {
                try {
                    Thread.sleep(500);
                } catch (InterruptedException ignored) {
                }
                System.out.println("Map Size:" + map.size());
                System.out.println(map.get("1"));
                System.out.println(map.get("2"));
                System.out.println(map.get("3"));
                System.gc();
            }
        }
    }

    运行结果(一直循环当中):

    Map Size:3   1 2 3

    Map Size:2    null 2 3

    根据String的特性,

    元素“1”的key已经没有地方引用了,所以进行了回收。

    元素“2”是被放在常量池中的,所以没有被回收。

    元素“3”因为还有变量s的引用,所以也没有进行回收。

    public class ss {
        public static void main(String[] args) {
            System.out.println(test());//cde
        }
        private static String test(){
            String a = new String("a");
            System.out.println(a);//a
            WeakReference<String> b = new WeakReference<String>(a);
            System.out.println(b.get());//a
            WeakHashMap<String, Integer> weakMap = new WeakHashMap<String, Integer>();
            weakMap.put(b.get(), 1);//{a=1}
            a = null;
            System.out.println("GC前b.get():"+b.get());//a
            System.out.println("GC前weakMap:"+weakMap);//{a=1}
            System.gc();
            System.out.println("GC后"+b.get());//null,a=null; System.gc()后,b!=null,b中的a也被系统回收了,
            System.out.println("GC后"+weakMap);//{}
            String c = "";
            try{
                c = b.get().replace("a", "b");//b.get()为null,会抛出异常。
                System.out.println("C:"+c);
                return c;
            }catch(Exception e){ 
                //finally有renturn,从finally退出。finally没有renturn,从try退出。
                //try 换成 catch 去理解就 OK 了
                c = "c";
                System.out.println("Exception");
                return c;
            }finally{
                c += "d";
                return c + "e";
            }
        }
    }
    public class WeakHashMapTest {  
        public static void main(String[] args) {  
            WeakHashMap w= new WeakHashMap();  
            //三个key-value中的key 都是匿名对象,没有强引用指向该实际对象  
            w.put(new String("语文"),new String("优秀"));  
            w.put(new String("数学"), new String("及格"));  
            w.put(new String("英语"), new String("中等"));  
            //增加一个字符串的强引用  
            w.put("java", new String("特别优秀"));  
            System.out.println(w);  //{java=特别优秀, 数学=及格, 英语=中等, 语文=优秀}
            //通知垃圾回收机制来进行回收  
            System.gc();  
            System.runFinalization();  
            //再次输出w
            System.out.println("第二次输出:"+w);  //第二次输出:{java=特别优秀}
        }    
    }

    spliteratorjava1.8引入的一种并行遍历的机制,Iterator提供也提供了对集合数据进行遍历的能力,但一个是顺序遍历,一个是并行遍历。

    OfInt sInt = Arrays.spliterator(arr, 2, 5);//下标,包头不包尾。截取。

    public static Spliterator.OfInt spliterator(int[] array, int startInclusive, int endExclusive) { return Spliterators.spliterator(array, startInclusive, endExclusive, Spliterator.ORDERED | Spliterator.IMMUTABLE); } public static Spliterator.OfInt spliterator(int[] array, int fromIndex, int toIndex, int additionalCharacteristics) { checkFromToBounds(Objects.requireNonNull(array).length, fromIndex, toIndex); return new IntArraySpliterator(array, fromIndex, toIndex, additionalCharacteristics); } public IntArraySpliterator(int[] array, int origin, int fence, int additionalCharacteristics) { this.array = array; this.index = origin; this.fence = fence; this.characteristics = additionalCharacteristics | Spliterator.SIZED | Spliterator.SUBSIZED; } public boolean tryAdvance(IntConsumer action) {//对分割后的数组依次调用函数 if (action == null) throw new NullPointerException(); if (index >= 0 && index < fence) { action.accept(array[index++]); return true; } return false; } public OfInt trySplit() {//返回分割的数组 int lo = index, mid = (lo + fence) >>> 1; return (lo >= mid) ? null : new IntArraySpliterator(array, lo, index = mid, characteristics); } public void forEachRemaining(IntConsumer action) { int[] a; int i, hi; // 每个元素执行给定的操作 if (action == null) throw new NullPointerException(); if ((a = array).length >= (hi = fence) && (i = index) >= 0 && i < (index = hi)) { do { action.accept(a[i]); } while (++i < hi); } } public interface IntConsumer { void accept(int value); default IntConsumer andThen(IntConsumer after) { Objects.requireNonNull(after); return (int t) -> { accept(t); after.accept(t); }; } } andThen方法是由IntConsumer 对象调用的,返回值是IntConsumer 类型:一个函数,首先调用 调用andThen方法的对象的accept()方法,然后调用after的accept方法。 public boolean tryAdvance(IntConsumer action) { if (action == null) throw new NullPointerException(); if (index >= 0 && index < fence) { action.accept(array[index++]); return true; } return false; } public class ffff { public static void main(String[] args) { int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; OfInt sInt = Arrays.spliterator(arr, 2, 5);// 返回3 4 5 IntConsumer consumer = new IntConsumer() { public void accept(int value) { System.out.println(value); } }; sInt.tryAdvance(consumer.andThen(new IntConsumer() {//先调用consumer的accept方法,在调用new IntConsumer()匿名内部类的accept方法, public void accept(int value) { System.out.println("i am after"); } })); sInt.tryAdvance(consumer.andThen(new IntConsumer() { public void accept(int value) { System.out.println("i am after"); } })); sInt.tryAdvance(consumer.andThen(new IntConsumer() { public void accept(int value) { System.out.println("i am after"); } })); sInt.tryAdvance(consumer.andThen(new IntConsumer() { public void accept(int value) { System.out.println("i am after"); } })); //3 i am after,4 i am after,5 i am after } public static void main3(String[] args) { int[] arr ={ 1, 2, 3, 4, 5, 6 }; IntConsumer consumer = new IntConsumer() {// IntConsumer的accept参数只能是int @Override public void accept(int value) { System.out.println(value); } }; OfInt sInt = Arrays.spliterator(arr);// sInt = {1,2,3,4,5,6} sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer);// 1,2,3,4,5,6 OfInt sInt1 = sInt.trySplit();// sInt1是sInt的前一半{1,2,3},sInt是后一半{4,5,6} sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer);// 只有4 5 6 sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer); sInt1.tryAdvance(consumer);// 只有1 2 3 } public static void main2(String[] args) { int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; OfInt sInt = Arrays.spliterator(arr, 2, 5);// 下标,包头不包尾。截取。 IntConsumer consumer = new IntConsumer() { @Override public void accept(int value) { System.out.println(value); } }; sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); /* if (index >= 0 && index < fence) { consumer.accept(array[index++]); index和fence是sInt的属性 */ sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer);// 只输出3 4 5 ,遍历截取后的元素。 } public static void main1(String[] args) { int[] arr = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; OfInt sInt = Arrays.spliterator(arr); IntConsumer consumer = new IntConsumer() { @Override public void accept(int value) { System.out.println(value); } }; sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer); sInt.tryAdvance(consumer);// 输出 1, 2, 3, 4, 5, 6, 7, 8, 9 } }

    Spliterator就是为了并行遍历元素而设计的一个迭代器,jdk1.8中的集合框架中的数据结构都默认实现了spliterator,可以和iterator顺序遍历迭代器一起看。

    Spliteratorsplitable iterator可分割迭代器)接口是Java为了并行遍历数据源中的元素而设计的迭代器,这个可以类比最早Java提供的顺序遍历迭代器Iterator,但一个是顺序遍历,一个是并行遍历

    第一个方法tryAdvance就是顺序处理每个元素,类似Iterator,如果还有元素要处理,则返回true,否则返回false

    第二个方法trySplit,这就是为Spliterator专门设计的方法,区分与普通的Iterator,该方法会把当前元素划分一部分出去创建一个新的Spliterator作为返回,两个Spliterator变会并行执行,如果元素个数小到无法划分则返回null二分法

    第三个方法estimateSize,该方法用于估算还剩下多少个元素需要遍历

    第四个方法characteristics,其实就是表示该Spliterator有哪些特性,用于可以更好控制和优化Spliterator的使用,具体属性你可以随便百度到,这里就不再赘言

    从最早Java提供顺序遍历迭代器Iterator时,那个时候还是单核时代,但现在多核时代下,顺序遍历已经不能满足需求了...如何把多个任务分配到不同核上并行执行,才是能最大发挥多核的能力,所以Spliterator应运而生啦

    对于Spliterator接口的设计思想,应该要提到的是Java7的Fork/Join(分支/合并)框架,总得来说就是用递归的方式把并行的任务拆分成更小的子任务,然后把每个子任务的结果合并起来生成整体结果。带着这个理解来看看Spliterator接口提供的方法

  • 相关阅读:
    LVS NAT模式
    lvs部署-DR模式
    Lvs原理
    iOS判断UIScrollView的滚动方向
    Swift开发必备技巧:内存管理、weak和unowned
    Swift2.0异常处理
    Swift中的延迟加载(懒加载)
    Swift和OC混编时, 关于@objc的作用
    Swift函数的定义建议
    Swift隐式可选型简单介绍
  • 原文地址:https://www.cnblogs.com/yaowen/p/11015129.html
Copyright © 2011-2022 走看看