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,拷贝,putAll,remove,containsValue,containsNullValue,forEach都会清理脏数据。
keySet,values,entrySet不会清理脏数据。
WeakHashMap是不同步的。可以使用 Collections.synchronizedMap 方法来构造同步的 WeakHashMap。
WeakHashMap的Key是弱引用,Value不是。WeakHashMap不会自动释放失效的弱引用table中 Entry,仅当包含了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=特别优秀} } }
spliterator是java1.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顺序遍历迭代器一起看。
Spliterator(splitable 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接口提供的方法