一 概念简介
在JVM内存中,一个对象拥有不同的引用类型,那这个对象在进行垃圾回收时会被执行不同的操作,从而影响这个对象的生命周期
1.1 强引用
强引用是使用最普遍的引用,我们平时代码中定义的引用都是强引用。如果一个对象具有强引用,垃圾回收器绝不会回收它,即使是内存空间不足时,Java虚拟机宁愿抛出OutOfMemoryError错误,使程序异常终止,也不会靠随意回收具有强引用的对象来解决内存不足问题。
1.2 软引用
如果一个对象只具有软引用,那就类似于可有可物的生活用品。如果内存空间足够,垃圾回收器就不会回收它,如果内存空间不足了,就会回收这些对象的内存。只要垃圾回收器没有回收它,该对象就可以被程序使用。软引用一般用于实现缓存对象的操作
软引用可以和一个引用队列(ReferenceQueue)联合使用,如果软引用所引用的对象被垃圾回收,Java虚拟机就会把这个软引用加入到与之关联的引用队列中
1.3 弱引用
弱引用是比软引用强度更低一种引用方式。如果一个对象只具有弱引用,他的生命周期更加短暂,那么当垃圾回收器线程在扫描他所管理的内存区域时,不管内存空间是否足够,都会将这个对象回收。不过垃圾回收器线程优先级比较低,因此只有弱引用的对象不一定会很快的被回收。
弱引用可以和一个引用队列(ReferenceQueue)联合使用,如果弱引用所引用的对象被垃圾回收,Java虚拟机就会把这个弱引用加入到与之关联的引用队列中。
1.4 虚引用
虚引用又叫做幽灵引用,就想他的名字一样,虚引用是形同虚设的,也就是虚引用相当于没有引用。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收。虚引用主要用于跟踪一个对象被垃圾回收的过程。
虚引用必须和引用队列(ReferenceQueue)联合使用(因为虚引用无法获得引用对象,不配合引用队列使用的话将毫无意义)。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。程序如果发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。
二 涉及的类
在java.lang.ref包中提供了三个类:SoftReference类、WeakReference类和PhantomReference类,他们分别对应软引用、弱引用和虚引用。这是三个类的父类都是Reference类,他们的方法基本上都一样。
2.1 软引用(SoftReference)
2.1.1 构造器
// 1.生成一个指向refrent对象的软引用
public SoftReference(T referent)
// 2.使用引用队列
public SoftReference(T referent, ReferenceQueue<? super T> q)
2.1.2 如何获得引用对象
// 通过get方法得到软引用所指向的对象
public T get()
如果这个引用指向的对象已经被回收,那么get( )将返回null
2.2 弱引用(WeakReference)
2.2.1 构造器
// 1.生成一个指向refrent对象的弱引用
public WeakReference(T referent)
// 2.使用引用队列
public WeakReference(T referent, ReferenceQueue<? super T> q)
2.2.2 如何获得引用对象
// 通过get方法得到弱引用所指向的对象
public T get()
如果这个引用指向的对象已经被回收,那么get( )将返回null
2.3 虚引用(PhantomReference)
2.3.1 构造器
// 使用引用队列(虚引用必须配合引用队列使用,否则没有意义)
public WeakReference(T referent, ReferenceQueue<? super T> q)
2.3.2 如何获得引用对象
// 通过get方法得到null?
public T get() {
return null;
}
虚引用不会获得对象的引用,所以重载了Reference的get方法,直接返回null。
2.4 引用队列
public class ReferenceQueue<T> {
public Reference<? extends T> poll() //将队列顶端的元素抛出
public Reference<? extends T> remove() // 将队列元素删除
}
三 测试demo
3.1 测试程序
package com.example.demo.test;
import java.lang.ref.*;
import java.util.LinkedList;
public class ReferenceTest {
private static ReferenceQueue<Source> rq = new ReferenceQueue<Source>();
public static void checkQueue() {
Reference<? extends Source> ref = null;
while ((ref = rq.poll()) != null) {
if (ref != null) {
if(ref instanceof SourceSoftReference) {
System.out.println("In queue: " + ((SourceSoftReference) (ref)).id);
} else if(ref instanceof SourceWeakReference) {
System.out.println("In queue: " + ((SourceWeakReference) (ref)).id);
} else {
System.out.println("In queue: " + ((SourcePhantomReference) (ref)).id);
}
}
}
}
public static void main(String args[]) {
// soft();
// weak();
phantom();
}
public static void soft() {
int size = 3;
LinkedList<SourceSoftReference> softList = new LinkedList<SourceSoftReference>();
for (int i = 0; i < size; i++) {
softList.add(new SourceSoftReference(new Source("Soft " + i), rq));
System.out.println("Just created soft: " + softList.getLast());
}
System.gc();
try { // 下面休息几分钟,让上面的垃圾回收线程运行完成
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < size; i++) {
System.out.println("output: " + softList.get(i).get());
}
checkQueue();
}
public static void weak() {
int size = 3;
LinkedList<SourceWeakReference> weakList = new LinkedList<SourceWeakReference>();
for (int i = 0; i < size; i++) {
weakList.add(new SourceWeakReference(new Source("Weak " + i), rq));
System.out.println("Just created weak: " + weakList.getLast());
}
System.gc();
try { // 下面休息几分钟,让上面的垃圾回收线程运行完成
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
for (int i = 0; i < size; i++) {
System.out.println("output: " + weakList.get(i).get());
}
checkQueue();
}
public static void phantom() {
int size = 3;
LinkedList<SourcePhantomReference> phantomList = new LinkedList<SourcePhantomReference>();
for (int i = 0; i < size; i++) {
phantomList.add(new SourcePhantomReference(new Source("Phantom " + i), rq));
System.out.println("Just created phantom: " + phantomList.getLast());
}
System.gc();
try { // 下面休息几分钟,让上面的垃圾回收线程运行完成
Thread.currentThread().sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// System.gc();
// try { // 下面休息几分钟,让上面的垃圾回收线程运行完成
// Thread.currentThread().sleep(2000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
for (int i = 0; i < size; i++) {
System.out.println("output: " + phantomList.get(i).get());
}
checkQueue();
}
}
class Source {
public String id;
public Source(String id) {
this.id = id;
}
protected void finalize() {
System.out.println("Finalizing Source " + id);
}
}
class SourceSoftReference extends SoftReference<Source> {
public String id;
public SourceSoftReference(Source Source, ReferenceQueue<Source> rq) {
super(Source, rq);
this.id = Source.id;
}
}
class SourceWeakReference extends WeakReference<Source> {
public String id;
public SourceWeakReference(Source Source, ReferenceQueue<Source> rq) {
super(Source, rq);
this.id = Source.id;
}
}
class SourcePhantomReference extends PhantomReference<Source> {
public String id;
public SourcePhantomReference(Source Source, ReferenceQueue<Source> rq) {
super(Source, rq);
this.id = Source.id;
}
}
3.2 分析
3.2.1 运行soft()方法
输出:
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
软引用仍然能获得对象的引用,且引用队列为空,说明内存充足,对象未被回收
3.2.2 运行weak()方法
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个弱引用,说明弱引用指向的对象已经被回收
3.2.2 运行phantom()方法
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
也就是说上例中虚引用指向的对象并没有被销毁
为什么执行了 System.gc()后对象没有被销毁??
因为对象重载了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方法,它会延缓对象被销毁的速度。