Reference简介
Reference继承自Object,有SoftReference、WeakReference、PhantomReference三个直接子类。
三个子类的构造方法中涉及到ReferenceQueue和Referent:
● Referent:被引用的对象
● ReferenceQueue:当引用(软引用/弱引用/虚引用)的Referent被回收后,该引用(软引用/弱引用/虚引用)会被enqueue到这个ReferenceQueue中
方便测试,我们新建一个类ReferenceTest,再创建一个静态内部类Person,重写内部类的 finalize() 方法[ GC collector回收对象时调用此方法 ]
public class ReferenceTest {
public static void main(String[] args) {
}
static class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("in Person finalize");
}
}
}
1. StrongReference强引用
Java中使用最多。普通的引用Object o = new Object(); Person person = new Person(); 等就是强引用。
强引用本身存储在栈中,new出来的对象存储在堆中。栈中保存的引用指向堆中对象的地址。
一般情况下,当引用不再指向堆中对象的地址时(如person = null;),GC collector就开始考虑对此内存(堆中的对象)进行回收。
Person person = new Person();
person就是是一个强引用,强引用不会被GC,即使内存不够抛出OOM时也不会被回收。
2. SoftReference软引用
软引用普通使用形式:
Person person = new Person();
SoftReference<Person> sr = new SoftReference<Person>(person);
强引用person作为参数,创建了一个软引用对象sr。看下面的例子:
private static void testSoftReference() {
Person person = new Person();
System.out.println("person对象为" + person);
SoftReference<Person> sr = new SoftReference<Person>(person);
person = null;//之前new出的Person对象不会立即被回收,除非JVM需要内存(OOM之前)
if (sr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + sr.get());
}
System.gc();
if (sr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + sr.get());
}
}
执行上述的例子,结果如下:
person对象为com.example.ReferenceTest$Person@511d50c0
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
①当执行person = null 后,堆内存的Person对象不再有任何强引用指向它,但此时还存在 sr引用的对象 指向Person对象。
此时调用sr.get()方法,返回Person对象,即之前堆中的强引用对象。我们可以合理猜测GC collector很有可能尚未进行垃圾回收,所以此时sr.get()方法返回不为空。
②我们继续执行System.gc() ,强制进行垃圾回收。打印结果可以看到,sr.get() 返回依然不为空,说明Person对象依旧没有被回收。
软引用所指向的对象要进行回收,需要满足两个条件:
● 没有任何强引用 指向 软引用指向的对象(内存中的Person对象)
● JVM需要内存时,即在抛出OOM之前
总结:SoftReference变相的延长了其指示对象占据堆内存的时间,直到虚拟机内存不足时垃圾回收器才回收此堆内存空间。
软引用还可以和一个ReferenceQueue一起使用, 当SoftReference的Referent被回收以后,这个SoftReference会被自动enqueue到这个ReferenceQueue中。
private static void testSoftReferenceWithQueue() {
Person person = new Person();
System.out.println("person对象为" + person);
ReferenceQueue<Person> queue = new ReferenceQueue<>();
SoftReference<Person> sr = new SoftReference<Person>(person, queue);
person = null;//之前new出的Person对象不会立即被回收,除非JVM需要内存(OOM之前)
if (sr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + sr.get());
}
System.out.println("加入ReferenceQueue的对象为" + queue.poll());
System.gc();
if (sr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + sr.get());
}
System.out.println("加入ReferenceQueue的对象为" + queue.poll());
}
执行上述的例子,结果如下:
person对象为com.example.ReferenceTest$Person@511d50c0
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
加入ReferenceQueue的对象为null
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
加入ReferenceQueue的对象为null
注意:当SoftReference或WeakReference的get()方法返回null时,仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。
而只有确认被垃圾回收后,如果有ReferenceQueue,其引用才会被放置于ReferenceQueue中。
3. WeakReference弱引用
弱引用的一般使用形式:
private static void testWeakReference() {
Person person = new Person();
System.out.println("person对象为" + person);
WeakReference<Person> wr = new WeakReference<>(person);
person = null;
//被GC后,之前new出的Person对象会立即被回收,进入GC流程
if (wr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + wr.get());
}
System.gc();
if (wr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + wr.get());
}
}
执行结果为:
person对象为com.example.ReferenceTest$Person@511d50c0
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
person对象进入GC流程
in Person finalize
①当执行person = null 后,堆内存的Person对象不再有任何强引用指向它,但此时还存在 wr引用的对象 指向Person对象。
此时调用wr.get()方法,返回Person对象,即之前堆中的强引用对象。我们可以合理猜测GC collector很有可能尚未进行垃圾回收,所以此时wr.get()方法返回不为空。
②我们继续执行System.gc() ,强制进行垃圾回收。打印结果可以看到,wr.get() 返回为空“person对象进入GC流程”,且执行了静态内部类中的finalize方法。说明Person对象被回收,进入垃圾回收流程。
弱引用所指向的对象要进行回收,只需要满足条件:
● 没有任何强引用 指向 弱引用指向的对象(内存中的Person对象)
总结:WeakReference不改变原有的强引用对象的垃圾回收机制。一旦其指示对象没有任何强引用对象时,此对象即进入正常的垃圾回收流程。
其主要使用场景见于:当前已有强引用指向强引用对象,此时由于业务需要,需要增加对此对象的引用,同时又不希望改变此引用的垃圾回收时机,此时WeakReference正好符合需求,常见于一些与生命周期的场景中,比如Activity中Handler的使用,为了防止内存泄漏需要用到弱引用。
与SoftReference一样,可以同ReferenceQueue一起使用。当WeakReference的Referent被回收以后,这个WeakReference会被自动enqueue到这个ReferenceQueue中。
private static void testWeakReferenceWithQueue() {
Person person = new Person();
System.out.println("person对象为" + person);
ReferenceQueue<Person> queue = new ReferenceQueue<>();
WeakReference<Person> wr = new WeakReference<Person>(person, queue);
System.out.println("wr对象为" + wr);
person = null;
if (wr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + wr.get());
}
System.out.println("Whether or not this reference has been enqueued: " + wr.isEnqueued());
System.out.println("queue item:" + queue.poll());
System.gc();
if (wr.get() == null) {//仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。只有确认被垃圾回收后,如果有ReferenceQueue,其引用才会被放置于ReferenceQueue中
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + wr.get());
}
try {
//确保垃圾回收线程能够执行
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Whether or not this reference has been enqueued: " + wr.isEnqueued());
System.out.println("queue item:" + queue.poll());
}
执行结果:
person对象为com.example.ReferenceTest$Person@511d50c0
wr对象为java.lang.ref.WeakReference@60e53b93
person对象尚未被回收com.example.ReferenceTest$Person@511d50c0
Whether or not this reference has been enqueued: false
queue item:null
person对象进入GC流程
in Person finalize
Whether or not this reference has been enqueued: true
queue item:java.lang.ref.WeakReference@60e53b93
从第二行和最后一行可以看出,person进入GC流程后,wr被加入了queue中。
注意:当SoftReference或WeakReference的get()方法返回null时,仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。
而只有确认被垃圾回收后,如果有ReferenceQueue,其引用才会被放置于ReferenceQueue中。
4.PhantomReference虚引用
我们来看看虚引用的源码:
package java.lang.ref;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
public class PhantomReference<T> extends Reference<T> {
public T get() {
return null;
}
public PhantomReference(T var1, ReferenceQueue<? super T> var2) {
super(var1, var2);
}
}
● PhantomReference只有一个构造函数PhantomReference(T referent, ReferenceQueue<? super T> q),因此,PhantomReference使用必须结合ReferenceQueue;
● 不管有无强引用指向PhantomReference的指示对象,PhantomReference的get()方法返回结果都是null。
看下面的例子:
private static void testPhantomReference() {
Person person = new Person();
System.out.println("person对象为" + person);
ReferenceQueue<Person> queue = new ReferenceQueue<>();
PhantomReference<Person> pr = new PhantomReference<>(person, queue);
System.out.println("pr对象为" + pr);
System.out.println("pr.get()=" + pr.get());
person = null;
System.gc();
try {
//确保垃圾回收线程能够执行
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("queue item:" + queue.poll());
}
我的执行结果为:
person对象为com.example.ReferenceTest$Person@511d50c0
pr对象为java.lang.ref.PhantomReference@60e53b93
pr.get()=null
in Person finalize
queue item: null
从第四行可以看出,Person对象已经进入了GC流程;第五行打印出的为null,说明GC流程还没有执行完,pr还未加入queue中。
理想的执行结果应该为:
person对象为com.example.ReferenceTest$Person@511d50c0
pr对象为java.lang.ref.PhantomReference@60e53b93
pr.get()=null
in Person finalize
queue item: java.lang.ref.PhantomReference@60e53b93
总结:与WeakReference一样,不改变原有的强引用对象的垃圾回收机制。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在 任何时候都可能被垃圾回收。 虚引用主要用来跟踪对象被垃圾回收的活动[ 监听并采取必要的行动 ]。
虚引用的用途:
当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之 关联的引用队列中。
程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。
程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
附件:整个测试类
package com.example;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference;
public class ReferenceTest {
public static void main(String[] args) {
//testSoftReference();
//testSoftReferenceWithQueue();
//testWeakReference();
//testWeakReferenceWithQueue();
//testPhantomReference();
}
/**
* 虚引用
*/
private static void testPhantomReference() {
Person person = new Person();
System.out.println("person对象为" + person);
ReferenceQueue<Person> queue = new ReferenceQueue<>();
PhantomReference<Person> pr = new PhantomReference<>(person, queue);
System.out.println("pr对象为" + pr);
System.out.println("pr.get()=" + pr.get());
person = null;
System.out.println("queue item:" + queue.poll());
System.gc();
try {
//确保垃圾回收线程能够执行
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("queue item: " + queue.poll());
}
/**
* 弱引用
*/
private static void testWeakReferenceWithQueue() {
Person person = new Person();
System.out.println("person对象为" + person);
ReferenceQueue<Person> queue = new ReferenceQueue<>();
WeakReference<Person> wr = new WeakReference<Person>(person, queue);
System.out.println("wr对象为" + wr);
person = null;
if (wr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + wr.get());
}
System.out.println("Whether or not this reference has been enqueued: " + wr.isEnqueued());
System.out.println("queue item:" + queue.poll());
System.gc();
if (wr.get() == null) {//仅是表明其指示的对象已经进入垃圾回收流程,此时对象不一定已经被垃圾回收。只有确认被垃圾回收后,如果有ReferenceQueue,其引用才会被放置于ReferenceQueue中
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + wr.get());
}
try {
//确保垃圾回收线程能够执行
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Whether or not this reference has been enqueued: " + wr.isEnqueued());
System.out.println("queue item:" + queue.poll());
}
/**
* 弱引用
*/
private static void testWeakReference() {
Person person = new Person();
System.out.println("person对象为" + person);
WeakReference<Person> wr = new WeakReference<>(person);
person = null;
//被GC后,之前new出的erson对象会立即被回收,进入GC流程
if (wr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + wr.get());
}
System.gc();
if (wr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + wr.get());
}
}
/**
* 软引用
*/
private static void testSoftReferenceWithQueue() {
Person person = new Person();
System.out.println("person对象为" + person);
ReferenceQueue<Person> queue = new ReferenceQueue<>();
SoftReference<Person> sr = new SoftReference<Person>(person, queue);
person = null;//之前new出的Person对象不会立即被回收,除非JVM需要内存(OOM之前)
if (sr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + sr.get());
}
System.out.println("加入ReferenceQueue的对象为" + queue.poll());
System.gc();
if (sr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + sr.get());
}
System.out.println("加入ReferenceQueue的对象为" + queue.poll());
}
/**
* 软引用
*/
private static void testSoftReference() {
Person person = new Person();
System.out.println("person对象为" + person);
SoftReference<Person> sr = new SoftReference<Person>(person);
person = null;//之前new出的Person对象不会立即被回收,除非JVM需要内存(OOM之前)
if (sr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + sr.get());
}
System.gc();
if (sr.get() == null) {
System.out.println("person对象进入GC流程");
} else {
System.out.println("person对象尚未被回收" + sr.get());
}
}
static class Person {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
protected void finalize() throws Throwable {
super.finalize();
System.out.println("in Person finalize");
}
}
}
参考链接:
http://www.cnblogs.com/lwbqqyumidi/p/4151833.html
http://www.cnblogs.com/zemliu/p/3333499.html
http://wenwen.sogou.com/z/q747019591.htm
http://docs.oracle.com/javase/8/docs/api/java/lang/ref/package-summary.html