JAVA 中各个 Reference 的研究与实践
最近在看netty的时候看到直接内存的相关概念,为了更详细的了解一下具体原理,搜到了一篇不错的文章
因为文章中又涉及到PhantomReference的概念,又为了更详细的了解一下具体原理,又搜到了另一篇不错的文章
这些概念很多文章中都有讲解,下面主要是针对各个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