- java的引用有四种,分别为强引用(StrongReference),软引用(SoftReference),弱引用(WeakReference),虚引用(PhantomReference),这四种引用的强度以此减弱。
强引用(Strong Reference)
- 强引用是指在程序代码中普遍存在的,例如
Object obj=new Object()
这类引用,若一个对象拥有强引用,且强引用还存在,那么垃圾收集器永远不会回收掉被应用的对象。当空间不足的时候,即使抛出OutOfMemoryError,也不会随意回收具有强引用的对象来解决内存不足的问题。
软引用 (Soft Reference)
- 软引用用来描述一些还有用却非必需的东西,对于软引用关联的对象,如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。若垃圾收集器没有将其回收,那么我们可以继续使用这些引用。JDK1.2以后,提供了
SoftReference
类来实现软引用。软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
public class SoftTest {
public static void main(String[] args) throws InterruptedException {
Object obj=new Object();
ReferenceQueue<Object> refQueue=new ReferenceQueue<>();//创建引用队列
SoftReference<Object> softReference=new SoftReference<>(obj,refQueue);//创建软引用
System.out.println(softReference.get());//java.lang.Object@1ddc4ec2
System.out.println(refQueue.poll());//null poll()是非阻塞的 队列中没有任何对象没有即输出null
obj=null;//清除强引用
System.gc(); //进行垃圾回收
System.out.println(softReference.get());//java.lang.Object@1ddc4ec2 内存足够 所以没有被回收
//如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
//remove()操作是阻塞的,只要引用队列中有对象才会进行操作。
//我们利用remove()可以查看对应的软引用是否被回收
//这里remove()一直处于阻塞,说明内存充足,软引用没有被回收。
System.out.println(refQueue.remove());
}
}
public class SoftTest {
public static void main(String[] args) throws InterruptedException {
Object obj=new Object();
ReferenceQueue<Object> refQueue=new ReferenceQueue<>();//创建引用队列
SoftReference<Object> softReference=new SoftReference<>(obj,refQueue);//创建软引用
System.out.println(softReference.get());//java.lang.Object@1ddc4ec2
System.out.println(refQueue.poll());//null poll()是非阻塞的 队列中没有任何对象没有即输出null
obj=null;//清除强引用
System.gc(); //进行垃圾回收
System.out.println(softReference.get());//java.lang.Object@1ddc4ec2 内存足够 所以没有被回收
//如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中。
//remove()操作是阻塞的,只要引用队列中有对象才会进行操作。
//我们利用remove()可以查看对应的软引用是否被回收
//这里remove()一直处于阻塞,说明内存充足,软引用没有被回收。
System.out.println(refQueue.remove());
}
}
弱引用(Weak Reference)
- 弱引用也是用来描述非必需的对象的,但是它的强度要更弱一些,被弱引用关联的对象只能生存到下一次垃圾回收之前,即当垃圾收集器工作时,无论当前内存是否充足,都会回收掉只被弱引用关联的对象。JDK1.2以后,提供了
WeakReference
类来实现弱引用。弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回 收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
public class WeakTest {
public static void main(String[] args) throws InterruptedException {
Object object=new Object();
ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
WeakReference<Object> weakReference=new WeakReference<>(object,referenceQueue);
System.out.println(weakReference.get()); //java.lang.Object@1ddc4ec2
System.out.println(referenceQueue.poll());//null
object=null;//清除强引用
System.gc();//进行gc
System.out.println(weakReference.get());//null 被回收
//如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
System.out.println(referenceQueue.remove()); //java.lang.ref.WeakReference@133314b
}
}
public class WeakTest {
public static void main(String[] args) throws InterruptedException {
Object object=new Object();
ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
WeakReference<Object> weakReference=new WeakReference<>(object,referenceQueue);
System.out.println(weakReference.get()); //java.lang.Object@1ddc4ec2
System.out.println(referenceQueue.poll());//null
object=null;//清除强引用
System.gc();//进行gc
System.out.println(weakReference.get());//null 被回收
//如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
System.out.println(referenceQueue.remove()); //java.lang.ref.WeakReference@133314b
}
}
- 上面代码我们就可以看到WeakReference与SoftReference的区别了。
虚引用(Phantom Reference)
- 虚引用也称幽灵引用或者幻影引用,它是最弱的一种引用关系。一个对象是否有虚引用的存在,完全不会对其生存的时间构成影响,也无法通过虚引用来取得一个对象实例(这句话什么意思呢:查看源码即可知道),如下:
public class PhantomReference<T> extends Reference<T> {
public T get() {
return null;
}
public PhantomReference(T var1, ReferenceQueue<? super T> var2) {
super(var1, var2);
}
}
public class PhantomReference<T> extends Reference<T> {
public T get() {
return null;
}
public PhantomReference(T var1, ReferenceQueue<? super T> var2) {
super(var1, var2);
}
}
- 虚引用的get()方法返回的是null,所以无法通过虚引用来取得一个对象实例。
- 那么虚引用有什么用? 为一个对象设置虚引用关联的唯一目的就是能在这个对象被垃圾收集器回收时收到一个系统通知。我们如何获得系统通知,我们观看上面代码即可知,一个虚引用必须和引用队列联合使用。当垃圾回收器回收一个对象时,如果发现它还有虚引用,就会把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。
public class PhantomTest {
public static void main(String[] args) throws InterruptedException {
Object object=new Object();
ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
PhantomReference<Object> phantomReference=new PhantomReference<>(object,referenceQueue);
System.out.println(phantomReference.get()); //null
System.out.println(referenceQueue.poll()); //null
object=null;
System.gc();
System.out.println(phantomReference.get()); //null
//上面两个get()说明 无法通过虚引用来取得一个对象实例
System.out.println(referenceQueue.remove()); //java.lang.ref.PhantomReference@1ddc4ec2
//为一个对象设置虚引用关联的唯一目的就是能在这个对象被垃圾收集器回收时收到一个系统通知
}
}
public class PhantomTest {
public static void main(String[] args) throws InterruptedException {
Object object=new Object();
ReferenceQueue<Object> referenceQueue=new ReferenceQueue<>();
PhantomReference<Object> phantomReference=new PhantomReference<>(object,referenceQueue);
System.out.println(phantomReference.get()); //null
System.out.println(referenceQueue.poll()); //null
object=null;
System.gc();
System.out.println(phantomReference.get()); //null
//上面两个get()说明 无法通过虚引用来取得一个对象实例
System.out.println(referenceQueue.remove()); //java.lang.ref.PhantomReference@1ddc4ec2
//为一个对象设置虚引用关联的唯一目的就是能在这个对象被垃圾收集器回收时收到一个系统通知
}
}
总结:
- GC要回收一个对象的时候,如果发现该对象有软、弱、虚引用的时候,会根据不同的情景,将这些引用加入到注册的引用队列中。
- 引用和引用队列联合使用可以提供一种通知机制,允许我们知道对象已经被销毁或者即将被销毁。
- 参考: 深入理解java虚拟机_JVM高级特性与最佳实践 周志明
- 参考: java中的4种reference的差别和使用场景