WeakReference & SoftReference & WeakHashMap

Reference

java.lang.ref包下面,继承于Reference类,帮助gc识别对象引用如何处理

  1. FinalReference: 强引用,即 Object obj = new Object(); 不会被强制回收,会引起OutOfMemory异常
  2. WeakReference, 每次gc都会被回收,不会引起OOM
  3. SoftReference,只有内存不够用时,才会被gc回收,不会引起OOM
  4. PhantomReference,不会被回收,会引起OOM,类似强引用。作用是在回收前,放入指定的ReferenceQueue,帮助跟踪

回收后,reference.get()会返回null指针


public class Test {

	private static AtomicInteger finalC = new AtomicInteger();
	public static class Bean {
		private byte[] bs = new byte[1024];
		public Bean() {
			byte b = 1;
			Arrays.fill(bs, b);
		}
		@Override
		protected void finalize() throws Throwable { 
			finalC.incrementAndGet();
		}
	}
	
	private ReferenceQueue<Bean> queue = new ReferenceQueue<Bean>();
	
	private void log(String phase) throws Exception {
		Field f = queue.getClass().getDeclaredField("queueLength");
		f.setAccessible(true);
		System.out.println(phase + "~~~, queue length:" + f.get(queue) + ", final num:" + finalC);
	}
	
	public void test() throws Exception {
		
		log("before");
		List<Reference> refs = new ArrayList<Reference>();
		for(int i = 0; i < 20480; ++i) {
			refs.add(new WeakReference<Test.Bean>(new Bean(), queue));
//			refs.add(new SoftReference<Test.Bean>(new Bean(), queue));
//			refs.add(new PhantomReference<Test.Bean>(new Bean(), queue));
			if(i % 3000 == 0) {
				int nullC = 0;
				for(int j = 0; j < i; ++j) {
					if(refs.get(j).get() == null) ++nullC;
				}
				log("number:" + i + ", null:" + nullC);
			}
		}
		log("2");
		System.gc();
		log("3");
		System.runFinalization();
		log("4");
	}
}


上述程序,跑在-Xmx=10m下,每个Bean > 1k, 循环跑 2048个,大于 20M


======   WeakReference

before~~~, queue length:0, final num:0
number:0, null:0~~~, queue length:0, final num:0
number:6000, null:4746~~~, queue length:4746, final num:4230
number:12000, null:9688~~~, queue length:9688, final num:9688
number:18000, null:17130~~~, queue length:15985, final num:15853
2~~~, queue length:17419, final num:18006
3~~~, queue length:17723, final num:18119
4~~~, queue length:19539, final num:20480


在6000个时,内存还未满,此时回收队列中已有4746个item已标识为NULL,并且4230个已经被finanlized


===== SoftReference

before~~~, queue length:0, final num:0
number:0, null:0~~~, queue length:0, final num:0
number:6000, null:0~~~, queue length:0, final num:0
number:12000, null:9197~~~, queue length:9197, final num:9197
number:18000, null:17722~~~, queue length:17722, final num:17722
2~~~, queue length:17722, final num:17722
3~~~, queue length:17722, final num:17722
4~~~, queue length:17722, final num:20480


在6000个时,内存未满,jvm不会强制回收SoftReference,直到12000已经超出内存xmx时


====== phantomReference

before~~~, queue length:0, final num:0
number:0, null:0~~~, queue length:0, final num:0
number:6000, null:6000~~~, queue length:0, final num:3294
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space


不会被强制回收,于是OOM



WeakHashMap

包含一个类对象成员: private final ReferenceQueue<K> queue = new ReferenceQueue<K>(); (构建Reference可以不指定队列,但map利用被回收ref放入队列的特性,做了特殊处理)
使用WeakHashMap.Entry extends WeakReference 构建entry: tab[i] = new Entry<K,V>(k, value, queue, h, e);
Entry即是一个弱指针,弱指向key对象。即weakReference.get() = key。 每次gc的时候,weakReference指向的entry.key为被回收置为空,并且entry将被放入referenceQueue。注意,此时map中的entry对象本事不为空,value不会空,只有key为空。所以直接获取entry是要出NPE的。
为此,每次获取entry时,需要通过ReferenceQueue清理table中key为null的entry:
private void expungeStaleEntries() {
	Entry<K,V> e;
        while ( (e = (Entry<K,V>) queue.poll()) != null) {
            // 将选定被回收的item,从hashmap集合中置空,跳过。防止hashmap返回null entry
        }
    }

同上面的例子,bean > 1K, 运行在  -Xmx10m下
private Map<String, Bean> map;
	
	public void test() throws Exception {
		map = new HashMap<String, Test.Bean>();
//		map = new WeakHashMap<String, Test.Bean>();
		
		for(int i = 1; i < 10240; ++i) {
			map.put(String.valueOf(i), new Bean());
			if(i%1000 == 0) {
				int size = 0;
				int npsize = 0;
				for(Entry<String, Test.Bean> entry : map.entrySet()) {
					++size;
					if(entry.getKey() == null || entry.getValue() == null) {
						npsize++;
					}
				}
				System.out.println("num: " + i + ", map size:" + size + ", null pointer:" + npsize);
			}
		}
	}

=============== HashMap 
num: 1000, map size:1000, null pointer:0
num: 2000, map size:2000, null pointer:0
......
num: 8000, map size:8000, null pointer:0
Exception in thread "main" java.lang.OutOfMemoryError: Java heap space

=============== WeakHashMap
num: 1000, map size:1000, null pointer:0
num: 2000, map size:2000, null pointer:0
num: 3000, map size:872, null pointer:0
num: 4000, map size:1872, null pointer:0
num: 5000, map size:604, null pointer:0
num: 6000, map size:1604, null pointer:0
num: 7000, map size:99, null pointer:0
num: 8000, map size:1099, null pointer:0
num: 9000, map size:123, null pointer:0
num: 10000, map size:1123, null pointer:0
可见,随着内存增加,entry被不停的会收,并且直接在map中被删除,不会留下空指针


原文地址:https://www.cnblogs.com/javawebsoa/p/3087260.html