zoukankan      html  css  js  c++  java
  • java总的强引用、软引用、弱引用、、虚引用

     
    一、为啥有四种引用?
      在Java的世界中,如果reference类型的数据中存储的数值代表的是另外一块内存的起始地址,就称这块内存代表着一个引用。 这种定义很纯粹,但是太过狭隘,一个对象在这种定义下只有被引用或者没有被引用两种状态,对于如何描述一些“食之无味,弃之可惜”的对象就显得无能为力。 我们希望能描述这样一类对象:当内存空间还足够时,则能保留在内存之中;如果内存空间在进行垃圾收集后还是非常紧张,则可以抛弃这些对象。 很多系统的缓存功能都符合这样的应用场景。

      所以,把对象的引用分为四种级别,从而使程序能更加灵活的控制对象的生命周期。这四种级别由高到低依次为:强引用、软引用、弱引用和虚引用。

    1、强引用(Strong Reference)

      强引用就是我们经常使用的引用,其写法如下:

     1 import java.io.IOException;
     2 
     3 public class T01_NormalReference {
     4     public static void main(String[] args) throws IOException {
     5         M m = new M();
     6         m = null;
     7         System.gc(); //DisableExplicitGC
     8 
     9         System.in.read();
    10     }
    11 }

    2、软引用(Soft Reference)
      如果一个对象只具有软引用,在内存足够时,垃圾回收器不会回收它;如果内存不足,就会回收这个对象的内存。

      使用场景: 图片缓存。图片缓存框架中,“内存缓存”中的图片是以这种引用保存,使得 JVM 在发生 OOM 之前,可以回收这部分缓存。

     1 /**
     2  * 软引用
     3  * 软引用是用来描述一些还有用但并非必须的对象。
     4  * 对于软引用关联着的对象,在系统将要发生内存溢出异常之前,将会把这些对象列进回收范围进行第二次回收。
     5  * 如果这次回收还没有足够的内存,才会抛出内存溢出异常。
     6  * -Xmx20M -Xms20M
     7  */
     8 
     9 import java.lang.ref.SoftReference;
    10 
    11 public class T02_SoftReference {
    12     public static void main(String[] args) {
    13         SoftReference<byte[]> m = new SoftReference<>(new byte[1024*1024*10]);
    14         //m = null;
    15         System.out.println(m.get());
    16         System.gc();
    17         try {
    18             Thread.sleep(500);
    19         } catch (InterruptedException e) {
    20             e.printStackTrace();
    21         }
    22         System.out.println(m.get());
    23 
    24         //再分配一个数组,heap将装不下,这时候系统会垃圾回收,先回收一次,如果不够,会把软引用干掉
    25         byte[] b = new byte[1024*1024*15];
    26         System.out.println(m.get());
    27     }
    28 }

    3、弱引用(Weak Reference)

      简单来说,就是将对象留在内存的能力不是那么强的引用。当垃圾回收器扫描到只具有弱引用的对象,不管当前内存空间是否足够,都会回收内存。

      使用场景:

      在下面的代码中,如果类 B 不是虚引用类 A 的话,执行 main 方法会出现内存泄漏的问题, 因为类 B 依然依赖于 A。

     1 public class Main {
     2     public static void main(String[] args) {
     3 
     4         A a = new A();
     5         B b = new B(a);
     6         a = null;
     7         System.gc();
     8         System.out.println(b.getA());  // null
     9 
    10     }
    11 
    12 }
    13 
    14 class A {}
    15 
    16 class B {
    17 
    18     WeakReference<A> weakReference;
    19 
    20     public B(A a) {
    21         weakReference = new WeakReference<>(a);
    22     }
    23 
    24     public A getA() {
    25         return weakReference.get();
    26     }
    27 }
    View Code

    4、虚引用(Phantom Reference)

      虚引用并不会决定对象的生命周期。如果一个对象仅持有虚引用,那么它就和没有任何引用一样,在任何时候都可能被垃圾回收器回收。可以理解为,他记录的JVM堆以外内存。

      虚引用与软引用和弱引用的一个区别在于:虚引用必须和引用队列(ReferenceQueue)联合使用。当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。

     1 /**
     2  *
     3  *
     4  *     一个对象是否有虚引用的存在,完全不会对其生存时间构成影响,
     5  *     也无法通过虚引用来获取一个对象的实例。
     6  *     为一个对象设置虚引用关联的唯一目的就是能在这个对象被收集器回收时收到一个系统通知。
     7  *     虚引用和弱引用对关联对象的回收都不会产生影响,如果只有虚引用活着弱引用关联着对象,
     8  *     那么这个对象就会被回收。它们的不同之处在于弱引用的get方法,虚引用的get方法始终返回null,
     9  *     弱引用可以使用ReferenceQueue,虚引用必须配合ReferenceQueue使用。
    10  *
    11  *     jdk中直接内存的回收就用到虚引用,由于jvm自动内存管理的范围是堆内存,
    12  *     而直接内存是在堆内存之外(其实是内存映射文件,自行去理解虚拟内存空间的相关概念),
    13  *     所以直接内存的分配和回收都是有Unsafe类去操作,java在申请一块直接内存之后,
    14  *     会在堆内存分配一个对象保存这个堆外内存的引用,
    15  *     这个对象被垃圾收集器管理,一旦这个对象被回收,
    16  *     相应的用户线程会收到通知并对直接内存进行清理工作。
    17  *
    18  *     事实上,虚引用有一个很重要的用途就是用来做堆外内存的释放,
    19  *     DirectByteBuffer就是通过虚引用来实现堆外内存的释放的。
    20  *
    21  */
    22 
    23 
    24 
    25 import java.lang.ref.PhantomReference;
    26 import java.lang.ref.Reference;
    27 import java.lang.ref.ReferenceQueue;
    28 import java.util.LinkedList;
    29 import java.util.List;
    30 
    31 public class T04_PhantomReference {
    32     private static final List<Object> LIST = new LinkedList<>();
    33     private static final ReferenceQueue<M> QUEUE = new ReferenceQueue<>();
    34 
    35 
    36 
    37     public static void main(String[] args) {
    38 
    39 
    40         PhantomReference<M> phantomReference = new PhantomReference<>(new M(), QUEUE);
    41 
    42 
    43         new Thread(() -> {
    44             while (true) {
    45                 LIST.add(new byte[1024 * 1024]);
    46                 try {
    47                     Thread.sleep(1000);
    48                 } catch (InterruptedException e) {
    49                     e.printStackTrace();
    50                     Thread.currentThread().interrupt();
    51                 }
    52                 System.out.println(phantomReference.get());
    53             }
    54         }).start();
    55 
    56         new Thread(() -> {
    57             while (true) {
    58                 Reference<? extends M> poll = QUEUE.poll();
    59                 if (poll != null) {
    60                     System.out.println("--- 虚引用对象被jvm回收了 ---- " + poll);
    61                 }
    62             }
    63         }).start();
    64 
    65         try {
    66             Thread.sleep(500);
    67         } catch (InterruptedException e) {
    68             e.printStackTrace();
    69         }
    70 
    71     }
    72 }
    import java.io.IOException;
       
      public class T01_NormalReference {
      public static void main(String[] args) throws IOException {
      M m = new M();
      m = null;
      System.gc(); //DisableExplicitGC
       
      System.in.read();
      }
      }
    ==========================================================================           如果您觉得这篇文章对你有帮助,可以【关注我】或者【点赞】,希望我们一起在架构的路上,并肩齐行
    ==========================================================================
  • 相关阅读:
    python 安装与pip安装
    使用通配符来解决数据1和11、12/13/14的问题
    数据库中一行变多行,拆分数据
    15-哈希表 HashTable
    13-自平衡二分搜索树 AVLTree
    12-并查集 UnionFind
    11-字典树 Trie
    10-线段树 Segment Tree
    09-堆 Heap(最大堆)
    08-映射 Map
  • 原文地址:https://www.cnblogs.com/amberJava/p/12621264.html
Copyright © 2011-2022 走看看