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