一、四种引用类型概述
在JDK1.2版本后,Java对引用的概念进行扩充,将引用分为强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)、虚引用(Phantom Reference)。这四种引用类型强度依次逐渐减弱,除了强引用外其他引用都继承自Reference抽象类,在java.lang.ref包中可以找到它们。
二、强引用(Strong Reference)
强引用是最传统的引用定义也是使用最频繁比如Object obj = new Object()这种引用赋值关系就是强引用。被强引用的对象只有在进行GC Roots对象不可达的情况下才会被垃圾回收器回收,否则即使在内存不足情况下甚至在JVM抛出OutOfMemory错误情况下也不回收。
三、软引用(Soft Reference)
软引用是指被软引用关联的对象,在没有其他强引用和在垃圾器回收垃圾时内存仍不足,再次垃圾回收会将这些被软引用的对象回收。由于这种特性软引用适合用来缓存,比如存放图片地址这样既能够提升查询效率,又可以避免内存溢出。
软引用使用方法如下,有时我们还可以通过配合引用队列(ReferenceQueue)来对数据进行监控或者释放引用对象本身。
public class SoftTest{
private static final int _4MB = 8*1024*1024;
public static void main(String arg[]){
List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i= 0;i<5;i++){
//当内存不足时进行垃圾回收,如果内存仍不足则再次垃圾回收,会回收被引用的byte[_4MB]对象,但不是全部。
SoftReference <byte[]> ref = new SoftReference<>(new byte[_4MB]);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
for (SoftReference<byte[]> ref :list){
System.out.println("--------------------------");
System.out.println(ref.get());
}
}
}
四、弱引用(Weak Reference)
弱引用是指被弱引用关联的对象,在没有强引用和在垃圾回收器回收垃圾时无论内存是否充足都会回收这些关联的对象。比如ThreadLocal和weakHashMap底层实现就使用到弱引用来防止内存泄漏。软引用使用方法如下,有时我们还可以通过配合引用队列(ReferenceQueue)来对数据进行监控或者释放引用对象本身。
private static final int _4MB = 10*1024*1024;
public static void main(String arg[]){
List<WeakReference<byte[]>> list = new ArrayList<>();
for (int i= 0;i<5;i++){
WeakReference <byte[]> ref = new WeakReference<>(new byte[_4MB]);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
for (WeakReference<byte[]> ref :list){
System.out.println("--------------------------");
System.out.println(ref.get());
}
}
这里有两个小细节需要注意:
1.只要进行GC回收弱引用关联的对象就会被回收,如果此时进行youngGC只回收年轻代的弱引用关联对象,在老年代的关联对象并不会回收除非触发了fullGC。
2.weakHashMap使用weakReference当做key来进行存储,当key中引用被gc回收它会自动把相应的entry给移除调,这个时候我们看到size实际上是会发生变化。
五、虚引用(Phantom Reference)
一个对象被虚引用所关联,并不会影响其生存时间,也不能通过虚引用来获得一个对象的实例必须配合引用队列使用。为一个对象关联虚引用的目的是在这个对象被回收时会触发一些任务来进行额外处理。比如Java NIO中DirectByteBuffer在分配直接内存时因不受jvm管理无法回收内存,此时内存的清理就是通过虚引用来完成。
DirectByteBuffer使用虚引用回收直接内存过程如下:
当创建DirectByteBuffer对象时会分配一块直接内存,同时会创建一个Cleanner虚引用对象然后将直接内存传递给这个虚引用。如果DirectByteBuffer对象没有被强引用所引用就会被回收,此时虚引用对象会被加入到引用队列,后台有一个Reference Handler线程定时从引用队列获取cleaner虚引用对象,执行这个对象的clean方法,clean方法执行了Unsafe.freeMemory方法根据直接内存地址找到对应内存进行释放。
五、引用队列(ReferenceQueue)
引用队列的作用是配合软引用、弱引用、虚引用的时候,当这些关联的对象被垃圾器回收时,引用对象本身就会被添加到引用队列,可以从引用队列获取这些引用对象进行额外工作处理,以下是引用队列配合软引用使用例子。
private static final int _4MB = 8*1024*1024;
public static void main(String arg[]){
ReferenceQueue<byte[]> queue = new ReferenceQueue<>();
List<SoftReference<byte[]>> list = new ArrayList<>();
for (int i= 0;i<5;i++){
//关联了引用队列,当byte[_4MB]对象被回收时,软引用自己会加入到queue中
SoftReference <byte[]> ref = new SoftReference<>(new byte[_4MB],queue);
System.out.println(ref.get());
list.add(ref);
System.out.println(list.size());
}
//队列中获取无用的软引用,并移除
Reference<? extends byte[]> poll = queue.poll();
System.out.println("--------------------------");
while (poll !=null){
System.out.println(poll.get());
list.remove(poll);
poll = queue.poll();
}
for (SoftReference<byte[]> ref :list){
System.out.println("--------------------------");
System.out.println(ref.get());
}
}