本文將談一下對(duì)SoftReference(軟引用)、WeakReference(弱引用)和PhantomRefrence(虛引用)的理解,這三個(gè)類是對(duì)heap中java對(duì)象的應(yīng)用,通過這個(gè)三個(gè)類可以和gc做簡(jiǎn)單的交互。
強(qiáng)引用:
除了上面提到的三個(gè)引用之外,還有一個(gè)引用,也就是最長(zhǎng)用到的那就是強(qiáng)引用.例如:
Java代碼
1.Object o=new Object();
2.Object o1=o;
|
上面代碼中第一句是在heap堆中創(chuàng)建新的Object對(duì)象通過o引用這個(gè)對(duì)象,第二句是通過o建立o1到new Object()這個(gè)heap堆中的對(duì)象的引用,這兩個(gè)引用都是強(qiáng)引用.只要存在對(duì)heap中對(duì)象的引用,gc就不會(huì)收集該對(duì)象.如果通過如下代碼:
Java代碼
1.o=null;
2.o1=null;
|
如果顯式地設(shè)置o和o1為null,或超出范圍,則gc認(rèn)為該對(duì)象不存在引用,這時(shí)就可以收集它了??梢允占⒉坏扔诰鸵粫?huì)被收集,什么時(shí)候收集這要取決于gc的算法,這要就帶來很多不確定性。例如你就想指定一個(gè)對(duì)象,希望下次gc運(yùn)行時(shí)把它收集了,那就沒辦法了,有了其他的三種引用就可以做到了。其他三種引用在不妨礙gc收集的情況下,可以做簡(jiǎn)單的交互。
heap中對(duì)象有強(qiáng)可及對(duì)象、軟可及對(duì)象、弱可及對(duì)象、虛可及對(duì)象和不可到達(dá)對(duì)象。應(yīng)用的強(qiáng)弱順序是強(qiáng)、軟、弱、和虛。對(duì)于對(duì)象是屬于哪種可及的對(duì)象,由他的最強(qiáng)的引用決定。如下:
Java代碼
1.String abc=new
String("abc"); //1
2.SoftReference<String>
abcSoftRef=new
SoftReference<String>(abc); //2
3.WeakReference<String> abcWeakRef =
new WeakReference<String>(abc);
//3
4.abc=null; //4
5.abcSoftRef.clear();//5
|
上面的代碼中:
第一行在heap對(duì)中創(chuàng)建內(nèi)容為“abc”的對(duì)象,并建立abc到該對(duì)象的強(qiáng)引用,該對(duì)象是強(qiáng)可及的。
第二行和第三行分別建立對(duì)heap中對(duì)象的軟引用和弱引用,此時(shí)heap中的對(duì)象仍是強(qiáng)可及的。
第四行之后heap中對(duì)象不再是強(qiáng)可及的,變成軟可及的。同樣第五行執(zhí)行之后變成弱可及的。
SoftReference(軟引用)
軟引用是主要用于內(nèi)存敏感的高速緩存。在jvm報(bào)告內(nèi)存不足之前會(huì)清除所有的軟引用,這樣以來gc就有可能收集軟可及的對(duì)象,可能解決內(nèi)存吃緊問題,避免內(nèi)存溢出。什么時(shí)候會(huì)被收集取決于gc的算法和gc運(yùn)行時(shí)可用內(nèi)存的大小。當(dāng)gc決定要收集軟引用是執(zhí)行以下過程,以上面的abcSoftRef為例:
1
首先將abcSoftRef的referent設(shè)置為null,不再引用heap中的new String("abc")對(duì)象。
2
將heap中的new String("abc")對(duì)象設(shè)置為可結(jié)束的(finalizable)。
3
當(dāng)heap中的new String("abc")對(duì)象的finalize()方法被運(yùn)行而且該對(duì)象占用的內(nèi)存被釋放,
abcSoftRef被添加到它的ReferenceQueue中。
注:對(duì)ReferenceQueue軟引用和弱引用可以有可無,但是虛引用必須有,參見:
Java代碼
Reference(T paramT, ReferenceQueue<? super
T>paramReferenceQueue)
|
被 Soft Reference
指到的對(duì)象,即使沒有任何 Direct Reference,也不會(huì)被清除。一直要到
JVM
內(nèi)存不足且
沒有 Direct Reference
時(shí)才會(huì)清除,SoftReference
是用來設(shè)計(jì) object-cache
之用的。如此一來 SoftReference
不但可以把對(duì)象 cache
起來,也不會(huì)造成內(nèi)存不足的錯(cuò)誤
(OutOfMemoryError)。我覺得
Soft Reference
也適合拿來實(shí)作 pooling
的技巧。
A obj = new A();
SoftRefenrence sr = new
SoftReference(obj);
引用時(shí)
if(sr!=null){
obj = sr.get();
}else{
obj = new A();
sr = new SoftReference(obj);
}
|
弱引用
當(dāng)gc碰到弱可及對(duì)象,并釋放abcWeakRef的引用,收集該對(duì)象。但是gc可能需要對(duì)此運(yùn)用才能找到該弱可及對(duì)象。通過如下代碼可以了明了的看出它的作用:
Java代碼
1.String abc=new String("abc");
2.WeakReference<String> abcWeakRef =
new
WeakReference<String>(abc);
3.abc=null;
4.System.out.println("before gc:
"+abcWeakRef.get());
5.System.gc();
6.System.out.println("after gc:
"+abcWeakRef.get());
|
運(yùn)行結(jié)果:
before gc: abc
after gc: null
gc收集弱可及對(duì)象的執(zhí)行過程和軟可及一樣,只是gc不會(huì)根據(jù)內(nèi)存情況來決定是不是收集該對(duì)象。
如果你希望能隨時(shí)取得某對(duì)象的信息,但又不想影響此對(duì)象的垃圾收集,那么你應(yīng)該用
Weak Reference
來記住此對(duì)象,而不是用一般的
reference。
A obj = new A();
WeakReference wr = new WeakReference(obj);
obj = null;
//等待一段時(shí)間,obj對(duì)象就會(huì)被垃圾回收
...
if (wr.get()==null) {
System.out.println("obj
已經(jīng)被清除了
");
} else {
System.out.println("obj
尚未被清除,其信息是 "+obj.toString());
}
...
}
|
在此例中,透過 get()
可以取得此 Reference
的所指到的對(duì)象,如果返回值為 null
的話,代表此對(duì)象已經(jīng)被清除。
這類的技巧,在設(shè)計(jì) Optimizer
或 Debugger
這類的程序時(shí)常會(huì)用到,因?yàn)檫@類程序需要取得某對(duì)象的信息,但是不可以
影響此對(duì)象的垃圾收集。
PhantomRefrence(虛引用)
虛顧名思義就是沒有的意思,建立虛引用之后通過get方法返回結(jié)果始終為null,通過源代碼你會(huì)發(fā)現(xiàn),虛引用通向會(huì)把引用的對(duì)象寫進(jìn)referent,只是get方法返回結(jié)果為null.先看一下和gc交互的過程在說一下他的作用.
1
不把referent設(shè)置為null,
直接把heap中的new String("abc")對(duì)象設(shè)置為可結(jié)束的(finalizable).
2
與軟引用和弱引用不同,
先把PhantomRefrence對(duì)象添加到它的ReferenceQueue中.然后在釋放虛可及的對(duì)象.
你會(huì)發(fā)現(xiàn)在收集heap中的new String("abc")對(duì)象之前,你就可以做一些其他的事情.通過以下代碼可以了解他的作用.
Java代碼
1.import java.lang.ref.PhantomReference;
2.import java.lang.ref.Reference;
3.import java.lang.ref.ReferenceQueue;
4.import java.lang.reflect.Field;
5.
6.public class Test {
7. public static boolean isRun
= true;
8.
9. public static void
main(String[] args) throws Exception {
10.
String abc =
new String("abc");
11.
System.out.println(abc.getClass()
+ "@" + abc.hashCode());
12.
final
ReferenceQueue referenceQueue = new
ReferenceQueue<String>();
13.
new Thread()
{
14.
public void
run() {
15.
while
(isRun) {
16.
Object o =
referenceQueue.poll();
17.
if (o !=
null) {
18.
try
{
19.
Field
rereferent = Reference.class
20.
.getDeclaredField("referent");
21.
rereferent.setAccessible(true);
22.
Object
result = rereferent.get(o);
23.
System.out.println("gc will collect:"
24.
| |