欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

JAVA 中各个 Reference 的研究与实践

程序员文章站 2022-04-16 21:15:05
Reference SoftReference WeakReference PhantomReference Cleaner ......

最近在看netty的时候看到直接内存的相关概念,为了更详细的了解一下具体原理,搜到了一篇不错的文章  

因为文章中又涉及到PhantomReference的概念,又为了更详细的了解一下具体原理,又搜到了另一篇不错的文章 

 

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

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

JAVA 中各个 Reference 的研究与实践

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

JAVA 中各个 Reference 的研究与实践

新标签页查看大图

JAVA 中各个 Reference 的研究与实践

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

JAVA 中各个 Reference 的研究与实践

JAVA 中各个 Reference 的研究与实践

JAVA 中各个 Reference 的研究与实践

JAVA 中各个 Reference 的研究与实践

Cleaner与DirectByteBuffer的部分源码

JAVA 中各个 Reference 的研究与实践

下面是根据各个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