您的位置:首页 > 其它

强、弱、软、虚引用

2015-08-08 13:19 363 查看
在Java中,虽然不需要程序员手动去管理对象的生命周期,但是如果希望某些对象具备一定的生命周期的话(比如内存不足时JVM就会自动回收某些对象从而避免OutOfMemory的错误)就需要用到软引用和弱引用了。
从Java SE2开始,就提供了四种类型的引用:强引用、软引用、弱引用和虚引用。Java中提供这四种引用类型主要有两个目的:第一是可以让程序员通过代码的方式决定某些对象的生命周期;第二是有利于JVM进行垃圾回收。
强引用:
强引用就是指在程序代码之中普遍存在的,比如下面这段代码中的object和str都是强引用:

Object object =
new
Object();


String str =
"hello"
;


只要某个对象有强引用与之关联,JVM必定不会回收这个对象,即使在内存不足的情况下,JVM宁愿抛出OutOfMemory错误也不会回收这种对象。如果想中断强引用和某个对象之间的关联,可以显示地将引用赋值为null。
软引用:
软引用是用来描述一些有用但并不是必需的对象,在Java中用java.lang.ref.SoftReference类来表示。对于软引用关联着的对象,只有在内存不足的时候JVM才会回收该对象。因此,这一点可以很好地用来解决OOM的问题,并且这个特性很适合用来实现缓存:比如网页缓存、图片缓存等。下面是一个使用示例:

public
class
Main {


public
static
void
main(String[] args){


SoftReference<String> str =
new
SoftReference<String>(
new
String(
"hello"
));


System.out.println(str.get());


}


}


软引用可以和一个引用队列(ReferenceQueue)联合使用,具体看后面,如果软引用所引用的对象被JVM回收,这个软引用就会被加入到与之关联的引用队列中。


弱引用:
弱引用也是用来描述非必需对象的,当JVM进行垃圾回收时,无论内存是否充足,都会回收被弱引用关联的对象,需要注意的是,这里所说的被弱引用关联的对象是指只有弱引用与之关联,如果存在强引用同时与之关联,则进行垃圾回收时也不会回收该对象(软引用也是如此)。在java中,用java.lang.ref.WeakReference类来表示。下面是使用示例:

public
class
Main {


public
static
void
main(String[] args){


WeakReference<String> sr =
new
WeakReference<String>(
new
String(
"hello"
));


System.out.println(sr.get());


System.gc();
//通知JVM的gc进行垃圾回收


System.out.println(sr.get());
//输出null,最好等一会再输出,确保垃圾回收站工作


}


}


弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被JVM回收,该弱引用就会被加入到与之关联的引用队列中。
弱引用一般会与强引用同时使用,当我们需要释放某个对象时,我们只需要释放该对象的强引用,弱引用会在垃圾回收时自动被回收,如果我们在多个地方对某个对象强引用,那么释放该对象时,需要每个地方都释放,如果有一个地方忘了释放都会导致内存泄露,弱引用的好处就是,避免我们手动释放某个对象的所有引用,只需要释放强引用便可。
集合元素是虚引用时,对象被回收后,集合元素并没有被回收,被回收的是元素中的对象,即elem.get()会返回null,但elem不为null,elem需要我们手动删掉,如下面代码:

public
static
void
main(String[] args)
throws
InterruptedException {


CopyOnWriteArraySet<WeakReference<String>>set =
new
CopyOnWriteArraySet<WeakReference<String>>();


String str1 =
new
String(
"str1"
);


String str2 =
new
String(
"str2"
);


String str3 =
new
String(
"str3"
);


set.add(
new
WeakReference<String>(str1));


set.add(
new
WeakReference<String>(str2));


set.add(
new
WeakReference<String>(str3));


for
(WeakReference<String>weakReference:set)


System.out.println(weakReference.get());


str1 =
null
;


str2 =
null
;


System.gc();


Thread.sleep(
100
);


System.out.println(set.size());


for
(WeakReference<String>weakReference:set){


if
(weakReference.get()==
null
)


set.remove(weakReference);


}


System.out.println(set.size());


}

输出:
str1
str2
str3
3
1
与ReferenceQueue结合使用:



public
static
void
main(String[] args)
throws
InterruptedException {


ReferenceQueue<Object> queue =
new
ReferenceQueue<Object>();


WeakReference<Object>wr =
new
WeakReference<Object>(
new
Integer(
10
),queue);


System.gc();


Thread.sleep(
1000
);


System.out.println(wr);


System.out.println(queue.poll());


}




输出:
java.lang.ref.WeakReference@c33f45e

java.lang.ref.WeakReference@c33f45e
对象Integer(10)被回收后,queue收到一个与之对应的弱引用,queue.poll()返回的是一个WeakReference对象,与wr是同一个对象,但不能通过该引用取得Integer(10)这个对象。
虽然知道对象被回收了,但拿不到被回收的对象,这又有什么用呢?
由于queue收到的对象与wr是同一个对象,所以我们可以继承WeakReference,然后把被回收的对象(也可以包括与之相关的其它属性)传递进去,通过queue.poll()方法取得wr之后,便可知道哪个对象被回收。
例子:

public
class
Test2 {


public
static
void
main(String[] args)
throws
InterruptedException {


ReferenceQueue<Object> queue =
new
ReferenceQueue<Object>();


/*


 
* 不能写成new MyWeakReference(new Integer(10),queue);


 
* 否则MyWeakReference这个对象本身被回收,这时queue.poll()返回的是null


 
*/


MyWeakReferencewr =
new
MyWeakReference(
new
Integer(
10
),queue);


System.gc();


Thread.sleep(
1000
);


MyWeakReferencemwr = (MyWeakReference)queue.poll();


if
(mwr!=
null
)


System.out.println(mwr.getObject());


}


private
static
class
MyWeakReference
extends
WeakReference<Object>{


private
int
num;


public
MyWeakReference(Integer integer,ReferenceQueue<Object> queue) {


super
(integer,queue);


this
.num = integer.intValue();


}


public
int
getObject(){


return
num;


}


}


}

MyWeakReference构造函数参数可随意,但通常至少要包括将要被回收的对象和ReferenceQueue对象,需要的时候可以传入其它参数。
queue是一个队列,可以接收多个MyWeakReference对象,如:

public
class
Test2 {


public
static
void
main(String[] args)
throws
InterruptedException {


ReferenceQueue<Object> queue =
new
ReferenceQueue<Object>();


MyWeakReferencewr =
new
MyWeakReference(
new
Integer(
10
),queue);


MyWeakReferencewr1 = 
new
MyWeakReference(
new
Integer(
11
),queue);


MyWeakReferencewr2 = 
new
MyWeakReference(
new
Integer(
12
),queue);


System.gc();


Thread.sleep(
1000
);


for
(Object x; (x = queue.poll()) !=
null
; ){


MyWeakReferencemwr = (MyWeakReference)x;


System.out.println(mwr.getObject());


}


}


private
static
class
MyWeakReference
extends
WeakReference<Object>{


private
int
num;


public
MyWeakReference(Integer integer,ReferenceQueue<Object> queue) {


super
(integer,queue);


this
.num = integer.intValue();


}


public
int
getObject(){


return
num;


}


}


}


虚引用:
虚引用也称为幽灵引用或幻影引用,它是最弱的一种引用关系,虚引用和前面的软引用、弱引用不同,它并不影响对象的生命周期。在java中用java.lang.ref.PhantomReference类表示。如果一个对象与虚引用关联,则跟没有引用与之关联一样,在任何时候都可能被垃圾回收器回收,所以无法通过虚引用来取得一个对象的实例,为一个对象设置虚引用的唯一目的就是希望在这个对象被回收时能收到一个系统通知。
要注意的是,虚引用必须和引用队列关联使用,当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之 关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。





public
static
void
main(String[] args)
throws
InterruptedException {


ReferenceQueue<String> queue =
new
ReferenceQueue<String>();


PhantomReference<String> pr =
new
PhantomReference<String>(
new
String(
"hello"
), queue);
//pr是hello这个对象的虚引用


System.gc();


Thread.sleep(
1000
);


System.out.println(pr);


System.out.println(queue.poll());


}

输出:


java.lang.ref.PhantomReference@2382815a
java.lang.ref.PhantomReference@2382815a



hello这个对象被gc回收了,queue中收到与之对应的虚引用,与pr是同一个对象,但不能通过该引用取得hello这个对象。
与弱引用一样,可以通过继承PhantomReference来把被回收的对象传递到pr中。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: