Java 中的引用类型我们都知道有 强引用、软引用(SoftReference)、弱引用(WeakReference)、虚引用(PhantomReference),除此之外,今天翻看JDK 8 【jdk1.8.0_172】源码时,还看到一种 FinalReference ,只不过这种引用类型是提供给JVM使用的,自己编程写代码用不上的。
Java 中引用相关的类定义在 java.lang.ref 包中,类图如下:
FinalReference、PhantomReference、WeakReference、SoftReference 类都继承抽象类Reference。引用队列类ReferenceQueue,在检测到适当的可到达性更改后,垃圾回收器将已注册的引用对象添加到该队列中。FinalizerHistogram类适用于 GC.finalizer_info 诊断命令支持,由VM调用。Java中的引用类型设计是跟垃圾回收器密切相关的。
强引用类型就是我们平常开发最普遍使用的,比如有个学生类Student,"Student s = new Student();",s 和new 出来的对象之间的关系就是强引用关系,只要强引用关系还存在,Java垃圾收集器就不会回收该对象。
软引用是用来描述程序中有用但是并非必需的对象。软引用对象,垃圾回收器会根据内存需求酌情清除这些对象。 软引用最常用于实现对内存敏感的缓存。对于软引用关联的对象,在系统将要发生内存溢出异常(OutOfMemoryError)之前,将会回收这些软引用对象。如果回收之后还没有足够内存,才会跑出内存溢出异常。
JDK 中使用SoftReference 类实现软引用。
构造方法:
//创建引用给定对象的新的软引用。
SoftReference(T referent)
//创建一个引用给定对象的新的软引用,并向给定队列注册该引用。
SoftReference(T referent, ReferenceQueue super T> q)
继承自Reference类的方法 get(),用于返回此软引用对象的指示对象:
/**
* Returns this reference object's referent. If this reference object has
* been cleared, either by the program or by the garbage collector, then
* this method returns null
.
*
* 返回此引用对象的引用对象。 如果此引用对象已被程序或垃圾收集器清除,然后
* 此方法返回 null code>。
*
* @return The object to which this reference refers, or
* null
if this reference object has been cleared
*/
public T get() {
T o = super.get();
if (o != null && this.timestamp != clock)
this.timestamp = clock;
return o;
}
弱引用对象也是用来描述程序中非必需的对象,但是它比软引用关系更弱。如果一个对象只被弱引用关联,那么它只能存活到下一次垃圾回收之前。Java垃圾回收器进行回收时,无论当前内存是否足够都会清理回收只被弱引用关联的对象。弱引用最常用于实现规范化映射。
JDK中使用WeakReference类实现弱引用:
public class WeakReference extends Reference {
/**
* Creates a new weak reference that refers to the given object. The new
* reference is not registered with any queue.
* 创建引用给定对象的新的弱引用。
* @param referent object the new weak reference will refer to
*/
public WeakReference(T referent) {
super(referent);
}
/**
* Creates a new weak reference that refers to the given object and is
* registered with the given queue.
* 创建引用给定对象的新的弱引用,并向给定队列注册该引用。
* @param referent object the new weak reference will refer to
* @param q the queue with which the reference is to be registered,
* or null if registration is not required
*/
public WeakReference(T referent, ReferenceQueue super T> q) {
super(referent, q);
}
}
有道面试题,可以加深对弱引用的理解:
public String test(){
String a = new String("a");
WeakReference b = new WeakReference(a);
WeakHashMap weakMap = new WeakHashMap();
weakMap.put(b.get(), 1);
a = null;
System.gc();
String c = "";
try{
c = b.get().replace("a", "b");
return c;
}catch(Exception e){
c = "c";
return c;
}finally{
c += "d";
return c + "e";
}
}
程序输出结果:
cde
大家可以分析一下,加深理解。
虚引用是最弱的一种引用关系,一个对象是否有虚引用的存在,完全不会对其生命周期构成影响,也用法无法通过虚引用获取对象。为了确保可回收的对象仍然保持原状,虚引用的指示对象不能被获取:虚引用的 get 方法总是返回 null。文档说虚引用最常见的用法是以某种可能比使用 Java finalization机制更灵活的方式来指派 pre-mortem 清除动作。
public class PhantomReference extends Reference {
/**
* Returns this reference object's referent. Because the referent of a
* phantom reference is always inaccessible, this method always returns
* null
.
* 返回此引用对象的指示对象。因为虚引用的指示对象总是不可到达的,所以此方法总是返回 null。
*
* @return null
*/
public T get() {
return null;
}
/**
* Creates a new phantom reference that refers to the given object and
* is registered with the given queue.
* 创建一个引用给定对象的新的虚引用,并向给定队列注册它。
*
* It is possible to create a phantom reference with a null
* queue, but such a reference is completely useless: Its get
* method will always return null and, since it does not have a queue, it
* will never be enqueued.
* 可能用一个 null 队列创建虚引用,但这样的引用是完全无用的:其 get 方法将总是返回 null,
* 同时,因为它没有队列,所以将永远无法把它加入队列中。
*
* @param referent the object the new phantom reference will refer to
* @param q the queue with which the reference is to be registered,
* or null if registration is not required
*/
public PhantomReference(T referent, ReferenceQueue super T> q) {
super(referent, q);
}
}
从构造方法中可以看出,PhantomReference 通常和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要进行垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。功能类似一种对象被垃圾回收器收集回收之前的通知。
FinalReference 是提供给Java虚拟机使用的,用于垃圾回收相关功能。该类和子类Finalizer访问权限都是同包访问(Package-private)的。InfoQ上有篇文章讲的很详细:JVM 源码分析之 FinalReference 完全解读