社区编辑申请
注册/登录
简单理解Java GC与幽灵引用
开发 后端
Java中一共有4种类型的引用:StrongReference、SoftReference、WeakReference以及PhantomReference (幽灵引用), 这 4 种类型的引用与Java GC有着密切的关系, 让我们逐一来看它们的定义和使用场景。

1. Strong Reference

StrongReference 是 Java 的默认引用实现,它会尽可能长时间的存活于 JVM 内, 当没有任何对象指向它时Java GC 执行后将会被回收

  1. @Test  
  2. public void strongReference() {   
  3. Object referent = new Object();   
  4.    
  5. /**  
  6.  * 通过赋值创建 StrongReference   
  7.  */  
  8. Object strongReference = referent;   
  9.    
  10. assertSame(referent, strongReference);   
  11.    
  12. referent = null;   
  13. System.gc();   
  14.    
  15. /**  
  16.  * StrongReference 在 GC 后不会被回收  
  17.  */  
  18. assertNotNull(strongReference);   
  19. }   
  20.  
  21.  @Test  
  22.  public void strongReference() {  
  23.   Object referent = new Object();  
  24.     
  25.   /**  
  26.    * 通过赋值创建 StrongReference   
  27.    */  
  28.   Object strongReference = referent;  
  29.     
  30.   assertSame(referent, strongReference);  
  31.     
  32.   referent = null;  
  33.   System.gc();  
  34.     
  35.   /**  
  36.    * StrongReference 在 GC 后不会被回收  
  37.    */  
  38.   assertNotNull(strongReference);  
  39.  } 

2. WeakReference & WeakHashMap

WeakReference, 顾名思义,是一个弱引用,当所引用的对象在 JVM 内不再有强引用时, Java GC 后 weak reference 将会被自动回收

  1. @Test  
  2. public void weakReference() {   
  3. Object referent = new Object();   
  4. WeakReference<Object> weakRerference = new WeakReference<Object>(referent);   
  5.  
  6. assertSame(referent, weakRerference.get());   
  7.    
  8. referent = null;   
  9. System.gc();   
  10.    
  11. /**  
  12.  * 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回收  
  13.  */  
  14. assertNull(weakRerference.get());   
  15. }   
  16.  
  17.  @Test  
  18.  public void weakReference() {  
  19.   Object referent = new Object();  
  20.   WeakReference<Object> weakRerference = new WeakReference<Object>(referent);  
  21.    
  22.   assertSame(referent, weakRerference.get());  
  23.     
  24.   referent = null;  
  25.   System.gc();  
  26.     
  27.   /**  
  28.    * 一旦没有指向 referent 的强引用, weak reference 在 GC 后会被自动回收  
  29.    */  
  30.   assertNull(weakRerference.get());  
  31.  } 

WeakHashMap 使用 WeakReference 作为 key, 一旦没有指向 key 的强引用, WeakHashMap 在Java GC 后将自动删除相关的 entry

  1. @Test  
  2. public void weakHashMap() throws InterruptedException {   
  3. Map<Object, Object> weakHashMap = new WeakHashMap<Object, Object>();   
  4. Object key = new Object();   
  5. Object value = new Object();   
  6. weakHashMap.put(key, value);   
  7.  
  8. assertTrue(weakHashMap.containsValue(value));   
  9.    
  10. key = null;   
  11. System.gc();   
  12.    
  13. /**  
  14.  * 等待无效 entries 进入 ReferenceQueue 以便下一次调用 getTable 时被清理  
  15.  */  
  16. Thread.sleep(1000);   
  17.    
  18. /**  
  19.  * 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry  
  20.  */  
  21. assertFalse(weakHashMap.containsValue(value));   
  22. }   
  23.  
  24.  @Test  
  25.  public void weakHashMap() throws InterruptedException {  
  26.   Map<Object, Object> weakHashMap = new WeakHashMap<Object, Object>();  
  27.   Object key = new Object();  
  28.   Object value = new Object();  
  29.   weakHashMap.put(key, value);  
  30.    
  31.   assertTrue(weakHashMap.containsValue(value));  
  32.     
  33.   key = null;  
  34.   System.gc();  
  35.     
  36.   /**  
  37.    * 等待无效 entries 进入 ReferenceQueue 以便下一次调用 getTable 时被清理  
  38.    */  
  39.   Thread.sleep(1000);  
  40.     
  41.   /**  
  42.    * 一旦没有指向 key 的强引用, WeakHashMap 在 GC 后将自动删除相关的 entry  
  43.    */  
  44.   assertFalse(weakHashMap.containsValue(value));  
  45.  } 

 3. SoftReference

SoftReference 于 WeakReference 的特性基本一致, ***的区别在于 SoftReference 会尽可能长的保留引用直到 JVM 内存不足时才会被回收(虚拟机保证), 这一特性使得 SoftReference 非常适合缓存应用

  1. @Test  
  2. public void softReference() {   
  3. Object referent = new Object();   
  4. SoftReference<Object> softRerference = new SoftReference<Object>(referent);   
  5.  
  6. assertNotNull(softRerference.get());   
  7.    
  8. referent = null;   
  9. System.gc();   
  10.    
  11. /**  
  12.  *soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用  
  13.  */  
  14. assertNotNull(softRerference.get());   
  15. }   
  16.  
  17.  @Test  
  18.  public void softReference() {  
  19.   Object referent = new Object();  
  20.   SoftReference<Object> softRerference = new SoftReference<Object>(referent);  
  21.    
  22.   assertNotNull(softRerference.get());  
  23.     
  24.   referent = null;  
  25.   System.gc();  
  26.     
  27.   /**  
  28.    *soft references 只有在 jvm OutOfMemory 之前才会被回收, 所以它非常适合缓存应用  
  29.    */  
  30.   assertNotNull(softRerference.get());  
  31.  } 

4. PhantomReference

作为本文主角, Phantom Reference(幽灵引用) 与 WeakReference 和 SoftReference 有很大的不同,因为它的 get() 方法永远返回 null, 这也正是它名字的由来

  1. @Test  
  2. public void phantomReferenceAlwaysNull() {   
  3. Object referent = new Object();   
  4. PhantomReference<Object> phantomReference = new PhantomReference<Object>(referent, new ReferenceQueue<Object>());   
  5.    
  6. /**  
  7.  * phantom reference 的 get 方法永远返回 null   
  8.  */  
  9. assertNull(phantomReference.get());   
  10. }   
  11.  
  12.  @Test  
  13.  public void phantomReferenceAlwaysNull() {  
  14.   Object referent = new Object();  
  15.   PhantomReference<Object> phantomReference = new PhantomReference<Object>(referent, new ReferenceQueue<Object>());  
  16.     
  17.   /**  
  18.    * phantom reference 的 get 方法永远返回 null   
  19.    */  
  20.   assertNull(phantomReference.get());  
  21.  } 

诸位可能要问, 一个永远返回 null 的 reference 要来何用,请注意构造 PhantomReference 时的第二个参数 ReferenceQueue(事实上 WeakReference & SoftReference 也可以有这个参数),
PhantomReference 唯一的用处就是跟踪 referent何时被 enqueue 到 ReferenceQueue 中.

5. RererenceQueue

当一个 WeakReference 开始返回 null 时, 它所指向的对象已经准备被回收, 这时可以做一些合适的清理工作. 将一个 ReferenceQueue 传给一个 Reference 的构造函数, 当对象被回收时, 虚拟机会自动将这个对象插入到 ReferenceQueue 中, WeakHashMap 就是利用 ReferenceQueue 来清除 key 已经没有强引用的 entries.

  1. @Test  
  2. public void referenceQueue() throws InterruptedException {   
  3. Object referent = new Object();  
  4. ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();   
  5. WeakReference<Object> weakReference = new WeakReference<Object>(referent, referenceQueue);   
  6.    
  7. assertFalse(weakReference.isEnqueued());   
  8. Reference<? extends Object> polled = referenceQueue.poll();   
  9. assertNull(polled);   
  10.    
  11. referent = null;   
  12. System.gc();   
  13.  
  14. assertTrue(weakReference.isEnqueued());   
  15. Reference<? extends Object> removed = referenceQueue.remove();   
  16. assertNotNull(removed);   
  17. }  
  18.  
  19.  @Test  
  20.  public void referenceQueue() throws InterruptedException {  
  21.   Object referent = new Object();    
  22.   ReferenceQueue<Object> referenceQueue = new ReferenceQueue<Object>();  
  23.   WeakReference<Object> weakReference = new WeakReference<Object>(referent, referenceQueue);  
  24.     
  25.   assertFalse(weakReference.isEnqueued());  
  26.   Reference<? extends Object> polled = referenceQueue.poll();  
  27.   assertNull(polled);  
  28.     
  29.   referent = null;  
  30.   System.gc();  
  31.  
  32.   assertTrue(weakReference.isEnqueued());  
  33.   Reference<? extends Object> removed = referenceQueue.remove();  
  34.   assertNotNull(removed);  
  35.  } 


6.PhantomReferencevs WeakReference

PhantomReference有两个好处, 其一, 它可以让我们准确地知道对象何时被从内存中删除, 这个特性可以被用于一些特殊的需求中(例如 Distributed GC,XWork 和 google-guice 中也使用 PhantomReference 做了一些清理性工作).

其二, 它可以避免 finalization 带来的一些根本性问题, 上文提到 PhantomReference 的唯一作用就是跟踪 referent 何时被 enqueue 到 ReferenceQueue 中,但是 WeakReference 也有对应的功能, 两者的区别到底在哪呢 ?
这就要说到 Object 的 finalize 方法, 此方法将在 gc 执行前被调用, 如果某个对象重载了 finalize 方法并故意在方法内创建本身的强引用,这将导致这一轮的 GC 无法回收这个对象并有可能
引起任意次 GC, ***的结果就是明明 JVM 内有很多 Garbage 却 OutOfMemory, 使用 PhantomReference 就可以避免这个问题, 因为 PhantomReference 是在 finalize 方法执行后回收的,也就意味着此时已经不可能拿到原来的引用,也就不会出现上述问题,当然这是一个很极端的例子, 一般不会出现.

7. 对比

Soft vs Weak vs Phantom References
Type Purpose Use When GCed Implementing Class
Strong Reference An ordinary reference. Keeps objects alive as long as they are referenced. normal reference. Any object not pointed to can be reclaimed. default
Soft Reference Keeps objects alive provided there’s enough memory. to keep objects alive even after clients have removed their references (memory-sensitive caches), in case clients start asking for them again by key. After a first gc pass, the JVM decides it still needs to reclaim more space. java.lang.ref.SoftReference
Weak Reference Keeps objects alive only while they’re in use (reachable) by clients. Containers that automatically delete objects no longer in use. After gc determines the object is only weakly reachable java.lang.ref.WeakReference 
java.util.WeakHashMap
Phantom Reference Lets you clean up after finalization but before the space is reclaimed (replaces or augments the use offinalize()) Special clean up processing After finalization. java.lang.ref.PhantomReference


8. Java GC小结
 一般的应用程序不会涉及到 Reference 编程, 但是了解这些知识会对理解Java GC 的工作原理以及性能调优有一定帮助, 在实现一些基础性设施比如缓存时也可能会用到, 希望本文能有所帮助.

【编辑推荐】

  1. 成为Java高手的重要过程
  2. Java GUI的发展和演化简史
  3. Java正则表达式工具类实例
  4. 用Java压缩文件解决没有中文问题示例
  5. Java与C++语言在作用域上的差异浅析
责任编辑:彭凡 来源: javaeye
相关推荐

2022-06-05 21:09:47

Python办公自动化

2022-06-29 09:19:09

静态代码C语言c代码

2022-06-22 08:02:11

2022-06-27 09:54:38

编程语言JavaC++

2022-06-01 11:14:42

Java代码技巧

2022-06-27 07:32:00

2022-06-22 10:23:42

互联网用户IE浏览器退休

2022-06-13 12:43:13

Java模块

2022-07-01 08:26:22

区块链去中心化以太坊

2022-06-30 09:07:52

2022-06-21 09:02:49

python技巧

2022-06-28 10:58:21

工具Java

2022-05-24 07:36:53

Java 8APIJava

2022-06-16 14:07:26

Java代码代码review

2022-06-29 10:16:25

数据库SQL

2022-06-21 09:26:21

Shell脚本JavaScript

2022-06-26 09:55:00

接口自动化项目

2022-06-17 12:10:07

RPA机器人流程自动化

2022-06-26 06:32:28

MySQL数据库维护

2022-04-20 10:56:06

JavaJVM参数

同话题下的热门内容

吐血推荐17个提升开发效率的“轮子”超全!Python 中常见的配置文件写法Pandas 新手容易犯的六个错误聊聊异步编程的七种实现方式使用 LSTM 对销售额预测(Python代码)Python进行数据可视化,你会用什么库来做呢?Spring MVC中@InitBinder注解是如何应用的?C语言和C++的区别和联系

编辑推荐

使用Kotlin做开发一个月后的感想面试官问你什么是消息队列?把这篇甩给他!五大自动化测试的Python框架图文详解两种算法:深度优先遍历(DFS)和广度优先遍历(BFS)2018年最流行的十大编程语言,其中包括你用的语言吗?
我收藏的内容
点赞
收藏

51CTO技术栈公众号