在JVM内存中,一个对象拥有不同的引用类型,那这个对象在进行垃圾回收时会被执行不同的操作,从而影响这个对象的生命周期
强引用是使用最普遍的引用,我们平时代码中定义的引用都是强引用。如果一个对象具有强引用,垃圾回收器绝不会回收它,即使是内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
如果一个对象只具有软引用,那就类似于可有可物的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用一般用于实现缓存对象的操作
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中
弱引用是比软引用强度更低一种引用方式。如果一个对象只具有弱引用,他的生命周期更加短暂,那么当垃圾回收器线程在扫描他所管理的内存区域时,不管内存空间是否足够,都会将这个对象回收。不过垃圾回收器线程优先级比较低,因此只有弱引用的对象不一定会很快的被回收。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
虚引用又叫做幽灵引用,就想他的名字一样,虚引用是形同虚设的,也就是虚引用相当于没有引用。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用于跟踪一个对象被垃圾回收的过程。
虚引用必须和引用队列(ReferenceQueue)联合使用(因为虚引用无法获得引用对象,不配合引用队列使用的话将毫无意义)。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
在java.lang.ref包中提供了三个类:SoftReference类、WeakReference类和PhantomReference类,他们分别对应软引用、弱引用和虚引用。这是三个类的父类都是Reference类,他们的方法基本上都一样。
// 1.生成一个指向refrent对象的软引用
public SoftReference(T referent)
// 2.使用引用队列
public SoftReference(T referent, ReferenceQueue super T> q)
// 通过get方法得到软引用所指向的对象
public T get()
如果这个引用指向的对象已经被回收,那么get( )将返回null
// 1.生成一个指向refrent对象的弱引用
public WeakReference(T referent)
// 2.使用引用队列
public WeakReference(T referent, ReferenceQueue super T> q)
// 通过get方法得到弱引用所指向的对象
public T get()
如果这个引用指向的对象已经被回收,那么get( )将返回null
// 使用引用队列(虚引用必须配合引用队列使用,否则没有意义)
public WeakReference(T referent, ReferenceQueue super T> q)
// 通过get方法得到null?
public T get() {
return null;
}
虚引用不会获得对象的引用,所以重载了Reference的get方法,直接返回null。
public class ReferenceQueue {
public Reference extends T> poll() //将队列顶端的元素抛出
public Reference extends T> remove() // 将队列元素删除
}
package com.example.demo.test;
import java.lang.ref.*;
import java.util.LinkedList;
public class ReferenceTest {
private static ReferenceQueue
输出:
Just created soft: com.example.demo.test.SourceSoftReference@eed1f14
Just created soft: com.example.demo.test.SourceSoftReference@7229724f
Just created soft: com.example.demo.test.SourceSoftReference@4c873330
output: com.example.demo.test.Source@119d7047
output: com.example.demo.test.Source@776ec8df
output: com.example.demo.test.Source@4eec7777
软引用仍然能获得对象的引用,且引用队列为空,说明内存充足,对象未被回收
Just created weak: com.example.demo.test.SourceWeakReference@eed1f14
Just created weak: com.example.demo.test.SourceWeakReference@7229724f
Just created weak: com.example.demo.test.SourceWeakReference@4c873330
Finalizing Source Weak 2
Finalizing Source Weak 1
Finalizing Source Weak 0
output: null
output: null
output: null
In queue: Weak 1
In queue: Weak 2
In queue: Weak 0
弱引用无法获得对象的引用,且引用队列中有3个弱引用,说明弱引用指向的对象已经被回收
Just created phantom: com.example.demo.test.SourcePhantomReference@eed1f14
Just created phantom: com.example.demo.test.SourcePhantomReference@7229724f
Just created phantom: com.example.demo.test.SourcePhantomReference@4c873330
Finalizing Source Phantom 2
Finalizing Source Phantom 1
Finalizing Source Phantom 0
output: null
output: null
output: null
虚引用无法获得对象的引用属于正常现象,但是引用队列为空不正常。这是为什么呢?
问题出在了source对象的finalize方法上,注释掉finalize方法,再运行结果如下:
Just created phantom: com.example.demo.test.SourcePhantomReference@eed1f14
Just created phantom: com.example.demo.test.SourcePhantomReference@7229724f
Just created phantom: com.example.demo.test.SourcePhantomReference@4c873330
output: null
output: null
output: null
In queue: Phantom 2
In queue: Phantom 0
In queue: Phantom 1
原因简单叙述如下:
在垃圾回收时:
弱引用:一旦探测对象只有弱引用,就会被插入到ReferenceQueue
虚引用:只有对象确实被GC销毁,才会被插入到ReferenceQueue
也就是说上例中虚引用指向的对象并没有被销毁
因为对象重载了finalize方法,需要执行2轮gc才能回收。
把phantom()方法的注释解开,执行2次GC后,输出如我们想象中一样
Just created phantom: com.example.demo.test.SourcePhantomReference@eed1f14
Just created phantom: com.example.demo.test.SourcePhantomReference@7229724f
Just created phantom: com.example.demo.test.SourcePhantomReference@4c873330
Finalizing Source Phantom 0
Finalizing Source Phantom 2
Finalizing Source Phantom 1
output: null
output: null
output: null
In queue: Phantom 1
In queue: Phantom 0
In queue: Phantom 2
不知道大家发现没有,通过虚引用的demo,我们不难发现,弱引用的demo中的Source对象也未必被回收掉了(一轮gc肯定不会被回收掉)。
所以,慎重重写finalize方法,它会延缓对象被销毁的速度。