先说结论:
首先,Java中有四种引用类型:强引用、软引用、弱引用、虚引用。-- 在 Java 1.2 中添加的,见 package java.lang.ref; 。
其次,这几个概念是与垃圾回收有关的。
然后,如果你不知道这几个概念,那你用的肯定都是强引用。例如 String str = new String(); 这个 str 到 new 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类。