Java中的四种引用

在JDK1.2之后,Java对引用进行了扩充,将引用分为四种形式

  • 强引用 StrongReference
  • 软引用 SoftReference
  • 弱引用 WeakReference
  • 虚引用 PhantomReference

他们的引用强度也就是被GC回收的可能依次减弱。

一、四中引用

1、强引用

最普遍的引用类型,只要强引用存在就一定不会被垃圾回收。

1
Object object=new Object();

内存不足时,会直接抛出OOM但是也不会回收强引用

2、软引用

软引用用来描述一些还有用但并非必需的对象。

如果一个对象只具有软引用,则内存空间足够,垃圾回收器就不会回收它;如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用可用来实现内存敏感的高速缓存。

示例:实现学生信息查询操作时有两套数据操作的方案

  1. 将得到的信息存放在内存中,后续查询则直接读取内存信息;
    • 优点:读取速度快
    • 缺点:内存空间一直被占,若资源访问量不高,则浪费内存空间
  2. 每次查询均从数据库读取,然后填充到TO返回。
    • 优点:内存空间将被GC回收,不会一直被占用
    • 在GC发生之前已有的TO依然存在,但还是执行了一次数据库查询,浪费IO

SoftReference的特点是它的一个实例保存对一个Java对象的软引用,该软引用的存在不妨碍垃圾收集线程对该Java对象的回收。也就是说,一旦SoftReference保存了对一个Java对象的软引用后,在垃圾线程对这个Java对象回收前,SoftReference类所提供的get()方法返回Java对象的强引用。另外,一旦垃圾线程回收该Java对象之后,get()方法将返回null。

1
2
3
4
5
MyObject aRef = new  MyObject();  
SoftReference aSoftRef=new SoftReference(aRef);
aRef = null;
ReferenceQueue queue = new ReferenceQueue();
SoftReference ref=new SoftReference(aMyObject, queue);

此时,对于这个MyObject对象,有两个引用路径,一个是来自SoftReference对象的软引用,一个来自变量aReference的强引用,所以这个MyObject对象是强可及对象。随即,我们可以结束aReference对这个MyObject实例的强引用.

此后,这个MyObject对象成为了软可及对象。如果垃圾收集线程进行内存垃圾收集,并不会因为有一个SoftReference对该对象的引用而始终保留该对象。

3、弱引用

弱引用与软引用的区别在于:只具有弱引用的对象拥有更短暂的生命周期。在垃圾回收器线程扫描它所管辖的内存区域的过程中,一旦发现了只具有弱引用的对象,不管当前内存空间足够与否,都会回收它的内存。不过,由于垃圾回收器是一个优先级很低的线程,因此不一定会很快发现那些只具有弱引用的对象。

弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。

4、虚引用

如果一个对象只具有虚引用,那么它就和没有任何引用一样,随时会被JVM当作垃圾进行GC。

为一个对象设置虚引用的唯一目的就是能再这个对象被垃圾回收的时候收到一个系统通知。

虚引用主要用来跟踪对象被垃圾回收器回收的活动。虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列 (ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。

5、可达性分析

那现在问题来了,若一个对象的引用类型有多个,那到底如何判断它的可达性呢?其实规则如下:

  1. 单条引用链的可达性以最弱的一个引用类型来决定;
  2. 多条引用链的可达性以最强的一个引用类型来决定;

我们假设图2中引用①和③为强引用,⑤为软引用,⑦为弱引用,对于对象5按照这两个判断原则,路径①-⑤取最弱的引用⑤,因此该路径对对象5的引用为软引用。同样,③-⑦为弱引用。在这两条路径之间取最强的引用,于是对象5是一个软可及对象

二、java.lang.ref包

关于引用的所有类都在这个包中

1、四种Reference

1
2
3
public abstract class Reference<T> {

}

可以看到Reference是继承自Object,其他三个引用都是它的子类

1
2
3
4
5
6
7
8
public class SoftReference<T> extends Reference<T> {
}

public class WeakReference<T> extends Reference<T> {
}

public class PhantomReference<T> extends Reference<T> {
}

2、ReferenceQueue

引用队列配合Reference的子类等使用,当引用对象所指向的内存空间被GC回收后,该引用对象则被追加到引用队列的末尾

作为一个Java对象,SoftReference对象除了具有保存软引用的特殊性之外,也具有Java对象的一般性。所以,当软可及对象被回收之后,虽然这个SoftReference对象的get()方法返回null,但这个SoftReference对象已经不再具有存在的价值,需要一个适当的清除机制,避免大量SoftReference对象带来的内存泄漏。在java.lang.ref包里还提供了ReferenceQueue。如果在创建SoftReference对象的时候,使用了一个ReferenceQueue对象作为参数提供给SoftReference的构造方法,如:

1
2
ReferenceQueue queue = new  ReferenceQueue();  
SoftReference ref=new SoftReference(aMyObject, queue);

那么当这个SoftReference所软引用的aMyOhject被垃圾收集器回收的同时,ref所强引用的SoftReference对象被列入ReferenceQueue。也就是说,ReferenceQueue中保存的对象是Reference对象,而且是已经失去了它所软引用的对象的Reference对象。另外从ReferenceQueue这个名字也可以看出,它是一个队列,当我们调用它的poll()方法的时候,如果这个队列中不是空队列,那么将返回队列前面的那个Reference对象。

在任何时候,我们都可以调用ReferenceQueue的poll()方法来检查是否有它所关心的非强可及对象被回收。如果队列为空,将返回一个null,否则该方法返回队列中前面的一个Reference对象。利用这个方法,我们可以检查哪个SoftReference所软引用的对象已经被回收。于是我们可以把这些失去所软引用的对象的SoftReference对象清除掉。常用的方式为:

1
2
3
4
SoftReference ref = null;  
while ((ref = (EmployeeRef) q.poll()) != null) {
// 清除ref
}

3、WeakHashMap

在Java集合中有一种特殊的Map类型—WeakHashMap,在这种Map中存放了键对象的弱引用,当一个键对象被垃圾回收器回收时,那么相应的值对象的引用会从Map中删除。WeakHashMap能够节约存储空间,可用来缓存那些非必须存在的数据。关于Map接口的一般用法。

WeakHashMap是以弱引用为键实现的HsahMap。更精确地说,对于一个给定的key对象,其对应的value对象的存在并不能阻止该key对象被GC。GC某个key对象时,它所在的条目会一并被从Map中删除。这是WeakHashMap与其他HashMap最大的不同。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public static void main(String[] args) throws InterruptedException {
WeakHashMap<String,String> weakHashMap=new WeakHashMap<String, String>();
String [] sts=new String[10];
for(int i=0;i<100;i++){
if(i%10==0) {
sts[i/10]=new String(""+i);
weakHashMap.put(sts[i/10], new String(""+i));
}
else
weakHashMap.put(new String(""+i), new String(""+i));
}

// 催促垃圾回收器工作
System.gc();
// 把CPU让给垃圾回收器线程
Thread.sleep(8000);

Iterator<String> iterator=weakHashMap.keySet().iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}

当执行System.gc()方法后,垃圾回收器只会回收那些仅仅持有弱引用的Key对象。id可以被10整除的Key对象持有强引用,因此不会被回收。

三、参考地址

http://www.cnblogs.com/fsjohnhuang/p/4268411.html

http://blog.csdn.net/lyfi01/article/details/6415726

http://blog.csdn.net/coolwxb/article/details/7939246

http://yizhenn.iteye.com/blog/2317184