zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    [多线程]使用信号量进行同步
    [多线程]互斥锁与信号量的区别
    [多线程]环形缓冲区以及多线程条件同步
    [多线程]LINUX c多线程编程-线程初始化与建立
    [STL]bitset使用
    [算法]败者树
    【Rollo的Python之路】Python:字符串内置函数
    【Rollo的Python之路】Python:字典的学习笔记
    【Rollo的Python之路】 购物车程序练习
    【Rollo的Python之路】Python 元组的学习
  • 原文地址:https://www.cnblogs.com/xiayudashan/p/9330477.html
Copyright © 2011-2022 走看看