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中被删除,不会留下空指针


 

 

 

你可能感兴趣的:(WeakReference)