在Java中,虽然不需要程序员手动去管理对象的生命周期,但是如果希望某些对象具备一定的生命周期的话(比如内存不足时JVM就会自动回收某些对象从而避免OutOfMemory的错误)就需要用到软引用和弱引用了。
从Java SE2开始,就提供了四种类型的引用:强引用、软引用、弱引用和虚引用。Java中提供这四种引用类型主要有两个目的:第一是可以让程序员通过代码的方式决定某些对象的生命周期;第二是有利于JVM进行垃圾回收。
Object object =
new
Object();
String str =
"hello"
;
软引用是用来描述一些有用但并不是必需的对象,在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回收,这个软引用就会被加入到与之关联的引用队列中。
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,最好等一会再输出,确保垃圾回收站工作
}
}
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());
}
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
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
*/
MyWeakReference wr =
new
MyWeakReference(
new
Integer(
10
),queue);
System.gc();
Thread.sleep(
1000
);
MyWeakReference mwr = (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;
}
}
}
public
class
Test2 {
public
static
void
main(String[] args)
throws
InterruptedException {
ReferenceQueue<Object> queue =
new
ReferenceQueue<Object>();
MyWeakReference wr =
new
MyWeakReference(
new
Integer(
10
),queue);
MyWeakReference wr1 =
new
MyWeakReference(
new
Integer(
11
),queue);
MyWeakReference wr2 =
new
MyWeakReference(
new
Integer(
12
),queue);
System.gc();
Thread.sleep(
1000
);
for
(Object x; (x = queue.poll()) !=
null
; ){
MyWeakReference mwr = (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());
}