Java的引用类

强引用与非强引用
[B]强引用 vs 非强引用[/B]

首先明晰一下是否强引用的区别,请看如下代码:
[code]Object obj = new Object();[/code]
如上代码执行之后,obj保留了新创建对象的“强引用”,作用是使得垃圾回收器侦测对象是否可回收的时候,能够知道此对象正在被使用,而不能够被释放。

有了强引用,自然会引出非强引用,在往下说之前,马背想和大家先思考一下为什么需要非强引用:既然我们知道强引用是让垃圾回收器不回收对象,而有限的内存在大量对象不被释放时,容易引发OutOfMemory错误,导致服务崩溃,到此,我们大约呈现出一个需求:在我们的程序里,可能设计出一些对象的生存周期是memory sensitive的,即如果内存足够,则对象不被释放,否则释放这些对象,但需要一种机制/架构让程序能够判断其状态,从而在程序中做出下一步的动作,比如重新创建/加载对象等。

对于上面这种需求,要求我们首先了解jvm中的垃圾回收器是如何判断对象是否可以回收的。

通过翻阅文档,我们可以知道垃圾回收器是根据对象的可到达性来判断其是否可以回收。这种可到达性,代表了jvm中对象的被引用的状态,如本文开头的强引用,即对应着强可到达对象。具体请详见:[url]http://gceclub.sun.com.cn/Java_Docs/jdk6/html/zh_CN/api/java/lang/ref/package-summary.html[/url]

每一种可达性状态,在程序中都有相应的方式来实现,马背在下面制作了一个表格,从上到下代表[b]从强到弱[/b]的过程:
[table]
|可到达性|含义|在lang.ref包中对应的对象|程序创建方式|
|强可到达对象|有强引用的对象|无|像文章开头那样:Object obj = new Object()|
|软可到达对象|没有强引用,只有软引用的对象|SoftReference|sr = new SoftReference(obj) 且 obj = null;|
|弱可到达对象|没有强/软引用,只有弱引用的对象|WeakReference|wr = new WeakReference(obj) 且 obj = null;|
|虚可到达对象|没有强/软/弱引用,只有虚引用的对象|PhantomReference|pr = new PhantomReference(obj) 且 obj = null;|
|不可到达对象|无任何引用的对象|无|强引用可调用obj = null;软/弱/虚引用可调用.clear()方法释放掉对象|
[/table]

下图是java.lang.ref包的类图

[img]http://dl.iteye.com/upload/attachment/204676/21cb8ca5-195a-32b1-9577-815ff0e9a97c.png[/img]

从上图中看到整个包结构非常清晰,Reference类做为一个抽象父类,定义了非强引用的几个典型行为:get,clear,isEnqueue,enqueue。而软/弱/虚引用分别用三个子类来实现,这里还有2个类是不对ref包外开放的:FinalReference和Finalizer。

下面我们对比下软/弱/虚三种引用的区别:

[table]
|非强引用类型|作用|
|SoftReference|软引用类型,当对象的可到达性是软可到达时候,对象是否被释放取决于垃圾回收器当时的判断,也就是说并不是遇到垃圾回收,软引用对象就一定会被释放。|
|WeakReference|弱引用类型,当对象是弱可到达时,垃圾回收器将释放此对象。但是注意垃圾回收器可能要运行多次才能找到并释放弱可及对象。|
|PhantomReference|虚引用类型,一种"假"的非强引用。当垃圾回收器判断其引用的对象为"虚可达到对象"时候,只是将虚引用对象放入ReferenceQueue中,但并不回收对象(但对象的finalize方法会被执行),直到对象进一步弱化到达“不可及对象”才会被回收。这样就给开发者提供了一种在对象被回收释放之前进行某种操作的机会,且不同于利用finalize方法这种方式,此外虚引用对象还有两个特点:
(A) 构建函数必须传入一个ReferenceQueue对象。
(B) get方法永远只返回null。|
[/table]

ok,我们来做个例子:


public class Entry {

private String name;


public Entry(String name){
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
public void finalize(){
System.out.println("==finalize:"+name);
}
}


public class TestRef {

public static void main(String[] argc) {
TestRef.testSoftReference();
TestRef.testWeakReference();
TestRef.testPhantomRefence();

}

private static void testPhantomRefence() {
Entry e = new Entry("phantom test ojbect");
ReferenceQueue rq = new ReferenceQueue();
PhantomReference pr = new PhantomReference(e, rq);
//phantomReference.get永远返回null
System.out.println("=PhantomReference.get()=" + pr.get());
//首先释放强引用
e = null;
//占用一些内存:)
Entry[] tempMemory = new Entry[78500]; //115555
for (int i = 0; i < 78500; i++) {
tempMemory[i] = new Entry(String.valueOf(i));
}
System.gc();
System.out.println("=PhantomReference.get()=" + pr.get());
System.out.println("=PhantomReference.isEnqueued()=" + pr.isEnqueued());
//如果之前已经显示执行了Entry的finalize(),则此处一定返回值
// 因为PhantomReference的enqueue是发生在finalize方法执行之前
System.out.println("=ReferenceQueue.poll()=" + rq.poll());

}


private static void testWeakReference() {
Entry e = new Entry("weak ojbect");
ReferenceQueue rq = new ReferenceQueue();
WeakReference wr = new WeakReference(e, rq);

//首先释放强引用
e = null;
System.gc();
System.out.println("=WeakReference.get()=" + wr.get());
System.out.println("=ReferenceQueue.poll()=" + rq.poll());

}

private static void testSoftReference() {
Entry e = new Entry("soft ojbect");
ReferenceQueue rq = new ReferenceQueue();
SoftReference sr = new SoftReference(e, rq);

//首先释放强引用
e = null;
//占用一些内存:)
Entry[] tempMemory = new Entry[78500]; //115555
for (int i = 0; i < 78500; i++) {
tempMemory[i] = new Entry(String.valueOf(i));
}
System.gc();
System.out.println("=SoftReference.get()=" + sr.get());
System.out.println("=ReferenceQueue.poll()=" + rq.poll());
}
}


好了,马背这里的运行环境为:Ubuntu 9.10
Sun JDK1.6.0_18
程序运行时候设置:java -Xms4097k -Xmx4097k

可以发现运行特性基本与上面的描述相同,只有一点需要注意:软/弱引用的被放入到ReferenceQueue中是发生在对象的finalize方法调用之后,而虚引用是在finalize调用之前。


在本文的参考资料中,例举了一种不正确的恢复weakReference对象的方式,摘录如下:

obj = wr.get();
if (obj == null)
{
wr = new WeakReference(recreateIt()); //1
obj = wr.get(); //2
}
//code that works with obj


错误的原因在如果在1,2步骤之间发生垃圾回收,则不能保证obj对象一定会被赋值正确。


参考资料:
[list]
[*]https://www.ibm.com/developerworks/cn/java/j-refs/
[*]http://weblogs.java.net/blog/enicholas/archive/2006/05/understanding_w.html
[/list]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值