Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

时间:2023-03-09 06:03:55
Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

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

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

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

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

Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

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

Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

新标签页查看大图

Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

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

Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

Cleaner与DirectByteBuffer的部分源码

Reference SoftReference WeakReference PhantomReference Cleaner 的研究与实践

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

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

 package xyz.fz.test;

 import sun.misc.Cleaner;

 import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.SoftReference;
import java.lang.ref.WeakReference; public class ReferenceTest { private static final int MB = 1024 * 1024; private static ReferenceQueue<Object> queue = new ReferenceQueue<>(); private static class BigBaby {
// just hold memory
private byte[] weight = new byte[20 * MB]; BigBaby() {
}
} private static class MyCleanerRunner implements Runnable {
private String somethingYouWant; MyCleanerRunner(String something) {
this.somethingYouWant = something;
} @Override
public void run() {
System.out.println(somethingYouWant);
}
} // -Xms64M -Xmx64M -XX:+PrintGCDetails -XX:+PrintGCDateStamps -XX:+PrintGCTimeStamps
public static void main(String[] args) {
System.out.println("============= softReferenceBigBabyGcNotWork =============");
System.gc();
softReferenceBigBabyGcNotWork();
System.out.println("============= softReferenceBigBabyGc =============");
System.gc();
softReferenceBigBabyGc();
System.out.println("============= weakReferenceBigBabyGc =============");
System.gc();
weakReferenceBigBabyGc();
System.out.println("============= phantomReferenceBigBabyGc =============");
System.gc();
phantomReferenceBigBabyGc();
System.out.println("============= cleanerBigBabyGc =============");
System.gc();
cleanerBigBabyGc();
System.out.println("============= queueFifoTest =============");
System.gc();
queueFifoTest();
} private static void softReferenceBigBabyGcNotWork() {
// 内存充足
// 软持有对象置空
// 主动gc
// 软持有对象没有被清除
BigBaby bigBaby = new BigBaby();
SoftReference<BigBaby> softReference = new SoftReference<>(bigBaby, queue);
bigBaby = null;
gcAndWait();
System.out.println("SoftReference's Referent: " + softReference.get());
printQueue();
} private static void softReferenceBigBabyGc() {
// 内存充足
// 软持有对象置空
// 创建新对象(导致内存不足)
// 主动(或被动)gc
// 软持有对象被清除,该软引用加入引用队列
BigBaby bigBaby = new BigBaby();
SoftReference<BigBaby> softReference = new SoftReference<>(bigBaby, queue);
bigBaby = null;
BigBaby bigBaby2 = new BigBaby();
BigBaby bigBaby3 = new BigBaby();
gcAndWait();
System.out.println("SoftReference's Referent: " + softReference.get());
printQueue();
} private static void weakReferenceBigBabyGc() {
// 内存充足
// 弱持有对象置空
// 主动gc
// 弱持有对象被清除,弱引用加入引用队列
BigBaby bigBaby = new BigBaby();
WeakReference<BigBaby> weakReference = new WeakReference<>(bigBaby, queue);
bigBaby = null;
gcAndWait();
System.out.println("WeakReference's Referent: " + weakReference.get());
printQueue();
} private static void phantomReferenceBigBabyGc() {
// 内存充足
// 幻持有对象置空
// 主动gc
// 幻持有对象没有被清除(内存充足的原因?),幻引用加入引用队列
BigBaby bigBaby = new BigBaby();
PhantomReference<BigBaby> phantomReference = new PhantomReference<>(bigBaby, queue);
bigBaby = null;
gcAndWait();
printQueue();
} private static void cleanerBigBabyGc() {
// 内存充足
// Cleaner持有对象置空
// 主动gc
// Cleaner持有对象被清除,Cleaner的runner被执行 /*
Cleaner是一个特殊的幻引用,
虽然它的构造中也有引用队列,但这个引用队列是个假引用队列,因为它从来不会被使用,仅仅作为幻引用的必要参数而已,
真正使用的是其定义的runner,
在持有对象被清除后runner得到执行
*/ /*
DirectByteBuffer用于分配堆外内存,
其中就有一个属性为cleaner,
并且该cleaner的持有对象就是其自身(DirectByteBuffer),
也就是说当这个DirectByteBuffer被gc回收之后,
cleaner中的runner方法将得到执行(对堆外内存进行回收)
*/
BigBaby bigBaby = new BigBaby();
Cleaner cleaner = Cleaner.create(bigBaby, new MyCleanerRunner("I'm Cleaner's runner. I can do what you want to do."));
bigBaby = null;
gcAndWait();
} private static void queueFifoTest() {
/*
从queue的名字会误认为是先进先出的队列,但是从实现和实验中可以看出他其实是后进先出
*/
BigBaby bigBaby = new BigBaby();
WeakReference<BigBaby> weakReference = new WeakReference<>(bigBaby, queue);
System.out.println(weakReference);
bigBaby = null;
gcAndWait(); bigBaby = new BigBaby();
WeakReference<BigBaby> weakReference2 = new WeakReference<>(bigBaby, queue);
System.out.println(weakReference2);
bigBaby = null;
gcAndWait(); bigBaby = new BigBaby();
WeakReference<BigBaby> weakReference3 = new WeakReference<>(bigBaby, queue);
System.out.println(weakReference3);
bigBaby = null;
gcAndWait(); printQueue();
} private static void printQueue() {
Object o;
int size = 0;
while ((o = queue.poll()) != null) {
System.out.println("Reference: " + o);
size++;
}
System.out.println("Reference Queue Size: " + size);
} private static void gcAndWait() {
System.gc();
try {
System.out.println("gc waiting ...");
Thread.sleep(1000L);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

 执行结果如下:

============= 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