Java的四种引用类型之弱引用

先说结论:

首先,Java中有四种引用类型:强引用、软引用、弱引用、虚引用。-- 在 Java 1.2 中添加的,见 package java.lang.ref;

其次,这几个概念是与垃圾回收有关的。

然后,如果你不知道这几个概念,那你用的肯定都是强引用。例如 String str = new String(); 这个 strnew String() 的引用类型就是强引用。

那么弱引用是什么?

弱引用,就是引用与对象之间的联系很弱,弱到垃圾回收器会无视这个引用,直接回收对象。

软引用与弱引用类似,但只在内存不足时才会被回收。

虚引用最差,甚至不能通过 get() 获取到对象。

为什么需要弱引用?

先看一段代码:

 

 1 package cn.larry.weakref.pojo;
 2 
 3 public final class Product {
 4     private String name;
 5     
 6     public Product() {
 7         super();
 8     }
 9 
10     public Product(String name) {
11         super();
12         this.name = name;
13     }
14 
15     public String getName() {
16         return name;
17     }
18 
19     public void setName(String name) {
20         this.name = name;
21     }
22 
23     @Override
24     public String toString() {
25         return "Product [name=" + name + "]";
26     }
27     
28 }
View Code

 

 1 package cn.larry.weakref.test;
 2 
 3 import java.lang.ref.ReferenceQueue;
 4 import java.lang.ref.WeakReference;
 5 import java.util.HashMap;
 6 import java.util.Map;
 7 import java.util.WeakHashMap;
 8 
 9 import org.junit.Test;
10 
11 import cn.larry.weakref.pojo.Product;
12 
13 public class WrTest {
14     private Product product = new Product("iPhone");
15 
16     @Test
17     public void strongRef() {
18         Map<Product, Integer> map = new HashMap<>();
19         map.put(product, 111);
20 
21         product = null;// 关键
22         map.keySet().forEach(System.out::println);// 对象仍然存在!
23     }
24 }

 

在上面的代码中,虽然在第21行已经将 product引用置空(null),但是第22行的代码仍然可以访问到该引用最初指向的对象!

这时,如果map中有多个Key,Product类又不可修改(不能添加识别字段),那你怎么确定并删除这个Key,能否自动移除该Key?弱引用就可以满足这样的要求。

一个经典的场景就是缓存,特别是缓存图片(加载到内存中),此时缓存肯定会包含一个指向内存中图片缓存的引用。问题在于,如果使用强引用,你必须决定何时不再需要该缓存并手动移除它。

怎么使用弱引用?

代码如下(与上面的测试代码在同一个 WrTest 类中):

 1     @Test
 2     public void weakRef_1() {
 3 
 4         ReferenceQueue<Product> rq = new ReferenceQueue<>();
 5         // WeakReference<Product> wr=new WeakReference<Product>(product);
 6         WeakReference<Product> wr = new WeakReference<Product>(product, rq);// 引用队列
 7         System.out.println("wr是否已被添加至引用队列:" + wr.isEnqueued());
 8 
 9         Map<WeakReference<Product>, Integer> map = new HashMap<>();
10         map.put(wr, 111);
11 
12         product = null;// 关键
13 //        System.gc();// 关键
14 
15         map.keySet().forEach(e -> System.out.println(e.get()));// 不gc()的话,还能访问对象,否则null。
16         System.out.println("wr是否已被添加至引用队列:" + wr.isEnqueued());
17     }

上面第4行构造了一个引用队列。

第5或6行构造了一个弱引用,区别在于是否指定引用队列。

如果指定了弱引用的引用队列,垃圾回收器会在回收对象之后将该弱引用添加至引用队列中 -- 可以在回收之后通过弱引用的 isEnqueued() 方法来判断。

需要注意:①必须将原强引用置空(第12行),且不能有其他强引用指向该对象;②必须主动调用垃圾回收器(第13行)。然后就能看到弱引用指向的对象已经不存在了(第15行) -- 弱引用本身还存在,所以map不为空。

但是,这时已经可以手动删除这个Key了。

更便捷的方法:

JDK提供了 WeakHashMap 可以更加方便的完成上面的工作,代码如下:

 1     @Test
 2     public void weakRef_2() {
 3         WeakHashMap<Product, Integer> map = new WeakHashMap<>();
 4         map.put(product, 111);
 5 
 6         product = null;// 关键
 7         // System.gc();//关键
 8 
 9         map.keySet().forEach(System.out::println);// 不gc()的话,还能访问对象,否则null。
10 
11     }

这种情况下,map.size()是0,即该Key自动被移除!!!

 

补充说明:

1、虽然可以主动调用垃圾回收器,但垃圾回收器并不总是会执行的。

2、Product类参考了 理解Java中的弱引用(Weak Reference) 中的pojo类。

 

 

参考

理解Java中的弱引用(Weak Reference)
译文:理解Java中的弱引用
 

posted on 2016-09-01 22:39  LarryZeal  阅读(2090)  评论(0编辑  收藏  举报

导航