序言:
Java对于引用概念经历过两个时期,JDK1.2之前和JDK1.2之后。在JDK1.2以前Java对于引用的定义很传统,如果reference类型的数据中存储的数值指定是另外一块内存的起始地址,就称为这块内存代表着一个引用.这个定义过于狭隘,在整个应用中对象就存在两种状态存在引用或者不存在。这样会产生一定的问题,对于我们应用系统存在于一些对象只在初始化使用一次(却被其它对象引用),存在一些对象是不经常被调用(也被其它对象引用),也存在一些对象是一直在被使用的。当内存资源足够的情况下,不会发生太大问题,一旦出现内存中资源紧缺,我们需要释放一些对象内存。我们肯定是期望这些只在初始化使用一次和不经常被调用的对象的内存释放出来。但是因为被引用的对象是不会被垃圾回收器给回收掉的,所以终止就会导致系统因内存而直接崩溃,这种现象我将其类比于以前的大锅饭(不论干不干活都有饭吃)。
针对于上述JDK1.2的状况Java在JDK1.2之后对于引用的概念细致化分为4种(让我们可以针对不同情况使用不同引用),分别为强引用(Strong Reference),软引用(Soft Reference),弱引用(Weak Reference),虚引用 (Phantom Reference).4种引用强度依次降低。java对于这四种引用的具体实现在java.lang.ref包下,如图所示。
类所在包情况:
完整类图:
2.1:强引用(Strong Reference)
java中最普遍存在的引用类型.例如我们在Object obj = new Object()时就会产生的这样的引用。在java应用中只要上述情况的强引用存在,垃圾回收器永远不会回收被引用对象。
2.2:软引用(Soft Reference)
软引用:可引用一些有用但却不是应用必须的对象,对于这些对象在应用内存资源不足(垃圾回收器已经进行过一次回收了),要发生内存溢出之前,垃圾回收器将这些对象列为可回收对象范围之内再进行第二次回收,如果此次回收内存资源还是不足,那么就会发生内存溢出溢出。在java中使用SoftReference类来实现软引用。
2.3:弱引用(Weak Reference)
弱引用和软引用的功能一样也是用以引用非必须的对象。但是它的强度比软引用更弱,不论jvm中内存是否充足,垃圾回收器在下一次垃圾回收的时候这些被弱引用的对象肯定会被回收。在java中使用WeakReference类来实现弱引用。
2.4:虚引用(Phantom Reference)
虚引用在四种引用类型中最弱的一种引用关系,它的存在对于对象的生存时间没有丝毫影响,也无法通过虚引用来获取一个对象的实例。它实际的作用是在当该对象被垃圾回收是可触发系统通知。在java中使用PhantomReference来实现虚引用。
2.5:引用队列 ReferenceQueue
通过查看SoftReference,WeakReference,PhantomReference三种不同类可以看到在初始化方法中都有ReferenceQueue的的影子,ReferenceQueue在其中充当的角色是用以实现在被三种引用对象被gc时,其它线程可以通过ReferenceQueue可以进行感知并作出一些处理。(后续有代码测试 )
3:代码应用:
3.1:强引用
/**
*强引用测试 为测试效果使用-Xmx10m -Xms10m
*/
public class StrongReferenceTest {
//堆内
public static List<Byte[]> datas = new ArrayList<>();
public static void main(String[] args) throws Exception {
for (int i=0;i<10;i++ ) {
datas.add(new Byte[1024*1024]);
Thread.sleep(100);
//触发gc
System.gc();
}
}
}
运行结果:
3.2:软引用测试
/**
* 软引用测试 设置内存大小为10m -Xmx10m -Xms10m
* @author fangyuan
*/
public class SoftReferenceTest {
public static List<SoftReference<Byte[]>> datas = new ArrayList<>();
public static void main(String[] args) throws Exception {
for (int i=0;i<5;i++ ) {
SoftReference<Byte[]> srf = new SoftReference<Byte[]>(new Byte[1024*512]);
datas.add(srf);
}
//验证数据是不是被gc掉
for (SoftReference softReference:datas) {
System.out.println(softReference.get());
}
}
}
运行结果:
从上述结果可以看到:当jvm感觉内存不足时(内存不足的定义是什么?与堆内存大小以及该引用对象get的时间都有关系),垃圾回收器会回收软引用对象。
3.3 弱引用测试
/**
* 弱引用测试 这里不设置最大内存大小
* @author fangyuan
*/
public class WeakReferenceTest {
public static List<WeakReference<Byte[]>> datas = new ArrayList<>();
public static void main(String[] args) throws Exception {
for (int i=0;i<5;i++ ) {
WeakReference<Byte[]> weakReference = new WeakReference<Byte[]>(new Byte[1024*512]);
datas.add(weakReference);
}
//验证数据
for (WeakReference weakReference:datas) {
System.out.print(weakReference.get());
}
System.out.println();
//触发gc
System.gc();
Thread.sleep(1000);
//验证数据是不是被gc掉
for (WeakReference weakReference:datas) {
System.out.print(weakReference.get());
}
}
}
结果:
测试结果发现,弱引用对象只能存活到下一次垃圾回收器开始工作之前,无关于现有系统内存大小。
3.4 虚引用
//源码
public class PhantomReference<T> extends Reference<T> {
//虚引用无法通过get方法获取对应实例对象
public T get() {
return null;
}
//初始化 配合ReferenceQueue做到被引用对象被gc之后能过通知用户线程 进行额外处理
public PhantomReference(T referent, ReferenceQueue<? super T> q) {
super(referent, q);
}
}
测试代码当gc虚引用时,用户线程得知并作出处理:
/**
* 测试ReferenceQueue 与PhantomReference 不设置内存最大值
* @author fangyuan
*/
public class PhantomReferenceTest {
public static List<PhantomReference<Byte[]>> datas = new ArrayList<>();
public static ReferenceQueue<Byte[]> referenceQueues = new ReferenceQueue<>();
public static void main(String[] args) throws Exception {
//设置
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int count =0 ;
while(true){
try {
Reference<? extends Byte[]> pr;
if ((pr = referenceQueues.remove()) !=null){
System.out.println("第"+(count++)+"个回收了对象"+pr);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
thread.setDaemon(true);
thread.start();
while (true) {
PhantomReference<Byte[]> phantomReference = new PhantomReference<Byte[]>(new Byte[1024*1024],referenceQueues);
datas.add(phantomReference);
}
}
}
测试结果: