java引用类型分析
java的引用类型
1、Reference
引用对象的抽象基类,定义了所有引用对象常见的操作方法。
// 构造函数
Reference(T referent) {
this(referent, null);
}
// 构造函数,定义引用队列
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent;
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
// 返回这个引用对象的referent,垃圾收集器回收后返回null
public T get() {
return this.referent;
}
public void clear() {
this.referent = null;
}
// 是否入队
public boolean isEnqueued() {
return (this.queue == ReferenceQueue.ENQUEUED);
}
// 入队方法
public boolean enqueue() {
return this.queue.enqueue(this);
}
2、ReferenceQueue
当垃圾回收器判断引用对象的可达性改变时(变成不可达),将其注册(入队)到应用队列中。
对其引用感兴趣的程序可以通过引用队列的poll、remove方法获取该引用。
// 入队
boolean enqueue(Reference<? extends T> r)
// 出队(非阻塞)
public Reference<? extends T> poll()
// 出队(阻塞)
public Reference<? extends T> remove()
3、WeakReference
垃圾回收器GC时,会回收弱引用绑定的对象,在同一时间或晚些时候将弱引用注册到引用队列。先清除对象,在入队列。
下面的测试代码说明弱引用的生命周期
public class Test<T extends Reference> {
static ReferenceQueue queue = new ReferenceQueue();
public T creatRefObject() {
Reference ref = new WeakReference(new Object(), queue);
return (T) ref;
}
public static void main(String[] args) throws Exception {
Test test = new Test();
Reference ref = test.creatRefObject();
System.gc();
// 引用中绑定的对象为null
System.out.println("reference get:" + ref.get());
// 获取队列中引用对象
System.out.println("queue poll:" + queue.remove());
}
}
输出结果:
reference get:null
queue poll:aaa@qq.com
4、SoftReference
当内存不足时(即将OOM时),会回收软引用绑定的对象,在同一时间或晚些时候将软引用注册到引用队列。先清除对象,在入队列。
下面的测试代码说明软引用的生命周期,需要设置运行时vm变量:-Xms20m -Xmx20m,模拟内存溢出。
public class Test<T extends Reference> {
static ReferenceQueue queue = new ReferenceQueue();
public T creatRefObject() {
Reference ref = new SoftReference(new Object(), queue);
return (T) ref;
}
private void oom() {
try {
List list = new ArrayList();
while (true) {
list.add(new Object());
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws Exception {
Test test = new Test();
Reference ref = test.creatRefObject();
System.gc();
// 引用中绑定的对象不为空
System.out.println("reference get:" + ref.get());
// 制造内存溢出
new Thread(()->{test.oom();}).start();
Thread.sleep(10000);
// 此时引用绑定对象为null
System.out.println("reference get:" + ref.get());
// 获取队列中引用对象
System.out.println("queue poll:" + queue.remove());
}
}
输出结果:
5、PhantomReference
与弱引用、软引用不同,虚引用对象不会自动回收(入队时并没有被垃圾回收器自动清除)。虚引用绑定的对象仍然保持原有状态,直到所有的引用都被清除,或者不可到达。
为了保持引用对象的原有状态,虚引用的get方法永远返回null
/**
* Returns this reference object's referent. Because the referent of a
* phantom reference is always inaccessible, this method always returns
* <code>null</code>.
*
* @return <code>null</code>
*/
public T get() {
return null;
}
下面的测试代码说明虚引用的生命周期
public class Test<T extends Reference> {
static ReferenceQueue queue = new ReferenceQueue();
public static class A {
public void print() {
System.out.println("I'am live!");
}
// @Override
// protected void finalize() throws Throwable {
// super.finalize();
// System.out.println("I'am finalize()!");
// }
}
public T creatRefObject() {
A a = new A();
Reference ref = new PhantomReference(a, queue);
a.print();
return (T) ref;
}
public static void main(String[] args) throws Exception {
Test test = new Test();
Reference ref = test.creatRefObject();
// gc先后都为null
System.out.println("ref get:" + ref.get());
System.gc();
System.out.println("ref get:" + ref.get());
Thread.sleep(500);
// 如果重写finalize方法需要再次调用gc
// System.gc();
// 获取队列中引用对象
System.out.println("queue poll:" + queue.remove());
}
}
输出结果:
6、Finalizer
当对象被标记回收时,如果重写了finalize方法,并且finalize方法没有被调用过,那么这个对象将会放置在一个叫做F-Queue的队列之中,低优先级的Finalizer线程去执行。
finalize()方法是对象逃脱死亡命运的最后一次机会,稍后GC将对F-Queue中的对象进行第二次小规模的标记,如果对象要在finalize()中成功拯救自己——只要重新与引用链上的任何一个对象建立关联即可,譬如把自己(this关键字)赋值给某个类变量或者对象的成员变量,那在第二次标记时它将被移除出“即将回收”的集合;如果对象这时候还没有逃脱,那基本上它就真的被回收了。
低优先级的Finalizer线程
private static class FinalizerThread extends Thread {
private volatile boolean running;
FinalizerThread(ThreadGroup g) {
super(g, "Finalizer");
}
public void run() {
if (running)
return;
// Finalizer thread starts before System.initializeSystemClass
// is called. Wait until JavaLangAccess is available
while (!VM.isBooted()) {
// delay until VM completes initialization
try {
VM.awaitBooted();
} catch (InterruptedException x) {
// ignore and continue
}
}
final JavaLangAccess jla = SharedSecrets.getJavaLangAccess();
running = true;
for (;;) {
try {
// 获取F-Queue对象,执行finalize方法
Finalizer f = (Finalizer)queue.remove();
f.runFinalizer(jla);
} catch (InterruptedException x) {
// ignore and continue
}
}
}
}