Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

最近在看netty的时候看到直接内存的相关概念,为了更详细的了解一下具体原理,搜到了一篇不错的文章 http://lovestblog.cn/blog/2015/05/12/direct-buffer/ 

因为文章中又涉及到PhantomReference的概念,又为了更详细的了解一下具体原理,又搜到了另一篇不错的文章 https://blog.csdn.net/xlinsist/article/details/57089288

这些概念很多文章中都有讲解,下面主要是针对各个Reference的具体实践(体现在最后的代码部分,各个实验都有相关注释,实验环境及所列源码均为jdk1.8.0_172)

Reference抽象类有两个构造方法,referent是必须参数,queue可选

Reference被类加载器加载初始化后会启动一个后台线程,这个线程的作用是将jvm传给它的Reference们加入到它们自己的queue中

新标签页查看大图

加入queue的Reference就可以被持有queue的对象进行相应的逻辑处理了,例如WeakHashMap会把不新鲜的对象给清除掉

Cleaner与DirectByteBuffer的部分源码

下面是根据各个Reference的定义做的一些具体实验(实验结果表明,Weak和Cleaner一遇到gc同时只剩下它们引用它们的referent的时候,这些referent一定会被清除掉)

实验记得加上main注释上的启动参数

  1 package xyz.fz.test;
  2 
  3 import sun.misc.Cleaner;
  4 
  5 import java.lang.ref.PhantomReference;
  6 import java.lang.ref.ReferenceQueue;
  7 import java.lang.ref.SoftReference;
  8 import java.lang.ref.WeakReference;
  9 
 10 public class ReferenceTest {
 11 
 12     private static final int MB = 1024 * 1024;
 13 
 14     private static ReferenceQueue<Object> queue = new ReferenceQueue<>();
 15 
 16     private static class BigBaby {
 17         // just hold memory
 18         private byte[] weight = new byte[20 * MB];
 19 
 20         BigBaby() {
 21         }
 22     }
 23 
 24     private static class MyCleanerRunner implements Runnable {
 25         private String somethingYouWant;
 26 
 27         MyCleanerRunner(String something) {
 28             this.somethingYouWant = something;
 29         }
 30 
 31         @Override
 32         public void run() {
 33             System.out.println(somethingYouWant);
 34         }
 35     }
 36 
 37     // -Xms64M -Xmx64M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps
 38     public static void main(String[] args) {
 39         System.out.println("============= softReferenceBigBabyGcNotWork =============");
 40         System.gc();
 41         softReferenceBigBabyGcNotWork();
 42         System.out.println("============= softReferenceBigBabyGc =============");
 43         System.gc();
 44         softReferenceBigBabyGc();
 45         System.out.println("============= weakReferenceBigBabyGc =============");
 46         System.gc();
 47         weakReferenceBigBabyGc();
 48         System.out.println("============= phantomReferenceBigBabyGc =============");
 49         System.gc();
 50         phantomReferenceBigBabyGc();
 51         System.out.println("============= cleanerBigBabyGc =============");
 52         System.gc();
 53         cleanerBigBabyGc();
 54         System.out.println("============= queueFifoTest =============");
 55         System.gc();
 56         queueFifoTest();
 57     }
 58 
 59     private static void softReferenceBigBabyGcNotWork() {
 60         // 内存充足
 61         // 软持有对象置空
 62         // 主动gc
 63         // 软持有对象没有被清除
 64         BigBaby bigBaby = new BigBaby();
 65         SoftReference<BigBaby> softReference = new SoftReference<>(bigBaby, queue);
 66         bigBaby = null;
 67         gcAndWait();
 68         System.out.println("SoftReference's Referent: " + softReference.get());
 69         printQueue();
 70     }
 71 
 72     private static void softReferenceBigBabyGc() {
 73         // 内存充足
 74         // 软持有对象置空
 75         // 创建新对象(导致内存不足)
 76         // 主动(或被动)gc
 77         // 软持有对象被清除,该软引用加入引用队列
 78         BigBaby bigBaby = new BigBaby();
 79         SoftReference<BigBaby> softReference = new SoftReference<>(bigBaby, queue);
 80         bigBaby = null;
 81         BigBaby bigBaby2 = new BigBaby();
 82         BigBaby bigBaby3 = new BigBaby();
 83         gcAndWait();
 84         System.out.println("SoftReference's Referent: " + softReference.get());
 85         printQueue();
 86     }
 87 
 88     private static void weakReferenceBigBabyGc() {
 89         // 内存充足
 90         // 弱持有对象置空
 91         // 主动gc
 92         // 弱持有对象被清除,弱引用加入引用队列
 93         BigBaby bigBaby = new BigBaby();
 94         WeakReference<BigBaby> weakReference = new WeakReference<>(bigBaby, queue);
 95         bigBaby = null;
 96         gcAndWait();
 97         System.out.println("WeakReference's Referent: " + weakReference.get());
 98         printQueue();
 99     }
100 
101     private static void phantomReferenceBigBabyGc() {
102         // 内存充足
103         // 幻持有对象置空
104         // 主动gc
105         // 幻持有对象没有被清除(内存充足的原因?),幻引用加入引用队列
106         BigBaby bigBaby = new BigBaby();
107         PhantomReference<BigBaby> phantomReference = new PhantomReference<>(bigBaby, queue);
108         bigBaby = null;
109         gcAndWait();
110         printQueue();
111     }
112 
113     private static void cleanerBigBabyGc() {
114         // 内存充足
115         // Cleaner持有对象置空
116         // 主动gc
117         // Cleaner持有对象被清除,Cleaner的runner被执行
118 
119         /*
120             Cleaner是一个特殊的幻引用,
121             虽然它的构造中也有引用队列,但这个引用队列是个假引用队列,因为它从来不会被使用,仅仅作为幻引用的必要参数而已,
122             真正使用的是其定义的runner,
123             在持有对象被清除后runner得到执行
124         */
125 
126         /*
127             DirectByteBuffer用于分配堆外内存,
128             其中就有一个属性为cleaner,
129             并且该cleaner的持有对象就是其自身(DirectByteBuffer),
130             也就是说当这个DirectByteBuffer被gc回收之后,
131             cleaner中的runner方法将得到执行(对堆外内存进行回收)
132         */
133         BigBaby bigBaby = new BigBaby();
134         Cleaner cleaner = Cleaner.create(bigBaby, new MyCleanerRunner("I'm Cleaner's runner. I can do what you want to do."));
135         bigBaby = null;
136         gcAndWait();
137     }
138 
139     private static void queueFifoTest() {
140         /*
141             从queue的名字会误认为是先进先出的队列,但是从实现和实验中可以看出他其实是后进先出
142         */
143         BigBaby bigBaby = new BigBaby();
144         WeakReference<BigBaby> weakReference = new WeakReference<>(bigBaby, queue);
145         System.out.println(weakReference);
146         bigBaby = null;
147         gcAndWait();
148 
149         bigBaby = new BigBaby();
150         WeakReference<BigBaby> weakReference2 = new WeakReference<>(bigBaby, queue);
151         System.out.println(weakReference2);
152         bigBaby = null;
153         gcAndWait();
154 
155         bigBaby = new BigBaby();
156         WeakReference<BigBaby> weakReference3 = new WeakReference<>(bigBaby, queue);
157         System.out.println(weakReference3);
158         bigBaby = null;
159         gcAndWait();
160 
161         printQueue();
162     }
163 
164     private static void printQueue() {
165         Object o;
166         int size = 0;
167         while ((o = queue.poll()) != null) {
168             System.out.println("Reference: " + o);
169             size++;
170         }
171         System.out.println("Reference Queue Size: " + size);
172     }
173 
174     private static void gcAndWait() {
175         System.gc();
176         try {
177             System.out.println("gc waiting ...");
178             Thread.sleep(1000L);
179         } catch (InterruptedException e) {
180             e.printStackTrace();
181         }
182     }
183 }

 执行结果如下:

============= softReferenceBigBabyGcNotWork =============
2018-07-18T17:24:34.874+0800: 0.239: [GC (System.gc()) [PSYoungGen: 3994K->1112K(18944K)] 3994K->1120K(62976K), 0.0012344 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:34.876+0800: 0.240: [Full GC (System.gc()) [PSYoungGen: 1112K->0K(18944K)] [ParOldGen: 8K->1015K(44032K)] 1120K->1015K(62976K), [Metaspace: 3467K->3467K(1056768K)], 0.0048045 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:34.890+0800: 0.255: [GC (System.gc()) [PSYoungGen: 327K->96K(18944K)] 21823K->21591K(62976K), 0.0004161 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:34.891+0800: 0.255: [Full GC (System.gc()) [PSYoungGen: 96K->0K(18944K)] [ParOldGen: 21495K->21389K(44032K)] 21591K->21389K(62976K), [Metaspace: 3469K->3469K(1056768K)], 0.0098622 secs] [Times: user=0.03 sys=0.02, real=0.01 secs]
gc waiting ...
SoftReference's Referent: xyz.fz.test.ReferenceTest$BigBaby@3b07d329
Reference Queue Size: 0
============= softReferenceBigBabyGc =============
2018-07-18T17:24:35.901+0800: 1.266: [GC (System.gc()) [PSYoungGen: 655K->64K(18944K)] 22045K->21453K(62976K), 0.0006612 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:35.902+0800: 1.267: [Full GC (System.gc()) [PSYoungGen: 64K->0K(18944K)] [ParOldGen: 21389K->900K(44032K)] 21453K->900K(62976K), [Metaspace: 3470K->3470K(1056768K)], 0.0119403 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
2018-07-18T17:24:35.927+0800: 1.292: [GC (Allocation Failure) [PSYoungGen: 0K->32K(18944K)] 41860K->41892K(62976K), 0.0012161 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:35.928+0800: 1.293: [Full GC (Ergonomics) [PSYoungGen: 32K->0K(18944K)] [ParOldGen: 41860K->41860K(44032K)] 41892K->41860K(62976K), [Metaspace: 3471K->3471K(1056768K)], 0.0043798 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:35.933+0800: 1.297: [GC (Allocation Failure) [PSYoungGen: 0K->0K(18944K)] 41860K->41860K(62976K), 0.0006057 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:35.933+0800: 1.298: [Full GC (Allocation Failure) [PSYoungGen: 0K->0K(18944K)] [ParOldGen: 41860K->21362K(44032K)] 41860K->21362K(62976K), [Metaspace: 3471K->3471K(1056768K)], 0.0130772 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
2018-07-18T17:24:35.948+0800: 1.313: [GC (System.gc()) [PSYoungGen: 0K->0K(18944K)] 41842K->41842K(62976K), 0.0005524 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:35.949+0800: 1.313: [Full GC (System.gc()) [PSYoungGen: 0K->0K(18944K)] [ParOldGen: 41842K->41842K(44032K)] 41842K->41842K(62976K), [Metaspace: 3471K->3471K(1056768K)], 0.0023415 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
gc waiting ...
SoftReference's Referent: null
Reference: java.lang.ref.SoftReference@41629346
Reference Queue Size: 1
============= weakReferenceBigBabyGc =============
2018-07-18T17:24:36.951+0800: 2.316: [GC (System.gc()) [PSYoungGen: 327K->32K(18944K)] 42170K->41874K(62976K), 0.0007930 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:36.952+0800: 2.317: [Full GC (System.gc()) [PSYoungGen: 32K->0K(18944K)] [ParOldGen: 41842K->882K(44032K)] 41874K->882K(62976K), [Metaspace: 3471K->3471K(1056768K)], 0.0072817 secs] [Times: user=0.00 sys=0.02, real=0.01 secs]
2018-07-18T17:24:36.962+0800: 2.326: [GC (System.gc()) [PSYoungGen: 327K->64K(18944K)] 21690K->21426K(62976K), 0.0004395 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:36.962+0800: 2.327: [Full GC (System.gc()) [PSYoungGen: 64K->0K(18944K)] [ParOldGen: 21362K->883K(44032K)] 21426K->883K(62976K), [Metaspace: 3471K->3471K(1056768K)], 0.0074164 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
gc waiting ...
WeakReference's Referent: null
Reference: java.lang.ref.WeakReference@404b9385
Reference Queue Size: 1
============= phantomReferenceBigBabyGc =============
2018-07-18T17:24:37.970+0800: 3.335: [GC (System.gc()) [PSYoungGen: 327K->64K(18944K)] 1210K->947K(62976K), 0.0004446 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:37.971+0800: 3.335: [Full GC (System.gc()) [PSYoungGen: 64K->0K(18944K)] [ParOldGen: 883K->883K(44032K)] 947K->883K(62976K), [Metaspace: 3471K->3471K(1056768K)], 0.0077789 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
2018-07-18T17:24:37.981+0800: 3.346: [GC (System.gc()) [PSYoungGen: 327K->96K(18944K)] 21691K->21459K(62976K), 0.0004417 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:37.981+0800: 3.346: [Full GC (System.gc()) [PSYoungGen: 96K->0K(18944K)] [ParOldGen: 21363K->21363K(44032K)] 21459K->21363K(62976K), [Metaspace: 3472K->3472K(1056768K)], 0.0042990 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
gc waiting ...
Reference: java.lang.ref.PhantomReference@6d311334
Reference Queue Size: 1
============= cleanerBigBabyGc =============
2018-07-18T17:24:38.986+0800: 4.351: [GC (System.gc()) [PSYoungGen: 327K->32K(18944K)] 21691K->21395K(62976K), 0.0004979 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:38.987+0800: 4.351: [Full GC (System.gc()) [PSYoungGen: 32K->0K(18944K)] [ParOldGen: 21363K->883K(44032K)] 21395K->883K(62976K), [Metaspace: 3472K->3472K(1056768K)], 0.0077382 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
2018-07-18T17:24:38.998+0800: 4.363: [GC (System.gc()) [PSYoungGen: 327K->160K(18944K)] 21691K->21523K(62976K), 0.0005646 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:38.999+0800: 4.364: [Full GC (System.gc()) [PSYoungGen: 160K->0K(18944K)] [ParOldGen: 21363K->884K(44032K)] 21523K->884K(62976K), [Metaspace: 3482K->3482K(1056768K)], 0.0077882 secs] [Times: user=0.06 sys=0.00, real=0.01 secs]
gc waiting ...
I'm Cleaner's runner. I can do what you want to do.
============= queueFifoTest =============
2018-07-18T17:24:40.007+0800: 5.372: [GC (System.gc()) [PSYoungGen: 655K->32K(18944K)] 1540K->924K(62976K), 0.0004796 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:40.008+0800: 5.372: [Full GC (System.gc()) [PSYoungGen: 32K->0K(18944K)] [ParOldGen: 892K->884K(44032K)] 924K->884K(62976K), [Metaspace: 3487K->3487K(1056768K)], 0.0079891 secs] [Times: user=0.05 sys=0.00, real=0.01 secs]
java.lang.ref.WeakReference@682a0b20
2018-07-18T17:24:40.018+0800: 5.383: [GC (System.gc()) [PSYoungGen: 327K->32K(18944K)] 21692K->21396K(62976K), 0.0005765 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:40.019+0800: 5.383: [Full GC (System.gc()) [PSYoungGen: 32K->0K(18944K)] [ParOldGen: 21364K->884K(44032K)] 21396K->884K(62976K), [Metaspace: 3487K->3487K(1056768K)], 0.0080978 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
gc waiting ...
java.lang.ref.WeakReference@3d075dc0
2018-07-18T17:24:41.029+0800: 6.394: [GC (System.gc()) [PSYoungGen: 327K->32K(18944K)] 21692K->21396K(62976K), 0.0006605 secs] [Times: user=0.00 sys=0.00, real=0.00 secs]
2018-07-18T17:24:41.030+0800: 6.395: [Full GC (System.gc()) [PSYoungGen: 32K->0K(18944K)] [ParOldGen: 21364K->884K(44032K)] 21396K->884K(62976K), [Metaspace: 3487K->3487K(1056768K)], 0.0083753 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
gc waiting ...
java.lang.ref.WeakReference@214c265e
2018-07-18T17:24:42.041+0800: 7.406: [GC (System.gc()) [PSYoungGen: 327K->32K(18944K)] 21692K->21396K(62976K), 0.0006599 secs] [Times: user=0.00 sys=0.02, real=0.00 secs]
2018-07-18T17:24:42.042+0800: 7.407: [Full GC (System.gc()) [PSYoungGen: 32K->0K(18944K)] [ParOldGen: 21364K->884K(44032K)] 21396K->884K(62976K), [Metaspace: 3487K->3487K(1056768K)], 0.0102298 secs] [Times: user=0.00 sys=0.00, real=0.01 secs]
gc waiting ...
Reference: java.lang.ref.WeakReference@214c265e
Reference: java.lang.ref.WeakReference@3d075dc0
Reference: java.lang.ref.WeakReference@682a0b20
Reference Queue Size: 3
Heap
PSYoungGen total 18944K, used 655K [0x00000000feb00000, 0x0000000100000000, 0x0000000100000000)
eden space 16384K, 4% used [0x00000000feb00000,0x00000000feba3f90,0x00000000ffb00000)
from space 2560K, 0% used [0x00000000ffd80000,0x00000000ffd80000,0x0000000100000000)
to space 2560K, 0% used [0x00000000ffb00000,0x00000000ffb00000,0x00000000ffd80000)
ParOldGen total 44032K, used 884K [0x00000000fc000000, 0x00000000feb00000, 0x00000000feb00000)
object space 44032K, 2% used [0x00000000fc000000,0x00000000fc0dd328,0x00000000feb00000)
Metaspace used 3494K, capacity 4564K, committed 4864K, reserved 1056768K
class space used 377K, capacity 388K, committed 512K, reserved 1048576K

原文地址:https://www.cnblogs.com/xiayudashan/p/9330477.html