CodeZLei

  博客园 :: 首页 :: 博问 :: 闪存 :: :: 联系 :: 订阅 订阅 :: 管理 ::

引用的应用场景

  1. 我们都知道垃圾回收器会回收符合回收条件的对象的内存,但并不是所有的程序员都知道回收条件取决于指向该对象的引用类型。这正是Java中弱引用和软引用的主要区别。
  2. 如果一个对象只有弱引用指向它,垃圾回收器会立即回收该对象,这是一种急切回收方式。
  3. 相对的,如果有软引用指向这些对象,则只有在JVM需要内存时才回收这些对象。
  4. 弱引用和软引用的特殊行为使得它们在某些情况下非常有用。
  5. 例如:软引用可以很好的用来实现缓存,当JVM需要内存时,垃圾回收器就会回收这些只有被软引用指向的对象。
  6. 而弱引用非常适合存储元数据,例如:存储ClassLoader引用。如果没有类被加载,那么也没有指向ClassLoader的引用。一旦上一次的强引用被去除,只有弱引用的ClassLoader就会被回收。

强引用(StrongReference)

  1. 强引用是我们在编程过程中使用的最简单的引用,如代码String s=”abc”中变量s就是字符串对象”abc”的一个强引用。
  2. 任何被强引用指向的对象都不能被垃圾回收器回收,这些对象都是在程序中需要的。
  3. StringBuffer stringBuffer = new StringBuffer(); 此时的object也是一个强引用。我们在开发过程中使用的最多的就是强引用。这个就不做过多的说明了。

软引用(SoftReference)

   软引用需要通过SoftReference类来实现,当一个对象只具有软引用时,它有可能被垃圾回收机制回收。对于只有软引用的对象而言,当系统内存空间足够时,它不会被系统回收,程序也可使用该对象;当系统内存空间不足时,系统将会回收它。软引用通常用于对内存敏感的程序中。

软引用案例
  1. 总结:软引用的代码执行过程和弱引用大致相同。根据打印的内容就说明了当JVM内存充足的时候,JVM是不会考虑去回收软引用指向的内存空间的。我们可以注意到,在上面的代码中,有一行代码是为str = null,虽然这行代码是不能阻止垃圾回收器回收对象,但是可以延迟回收,这点和弱引用是不相同的。这也是软引用适合做缓存而弱引用适合存储元数据的根本原因所在。
  2. 一个使用弱引用的典型例子是WeakHashMap,它是除HashMap和TreeMap之外,Map接口的另一种实现。WeakHashMap有一个特点:map中的键值(keys)都被封装成弱引用,也就是说一旦强引用被删除,WeakHashMap内部的弱引用就无法阻止该对象被垃圾回收器回收。

弱引用(WeakReference)

  1. 弱引用通过WeakReference类实现,弱引用和软引用很像但是弱引用的级别更低。
  2. 对于只有弱引用的对象而言,当系统垃圾回收机制运行时,不管系统内存是否足够,总会回收该对象所占用的内存(立即回收的方式)。当然并不是说当一个对象只有弱引用时,它就会立即被回收—-正如那些使用引用的对象一样,必须等到系统垃圾回收机制运行时才会被回收。

    public class Main {
        public static void main(String[] args) {
            String str = new String("Struts2权威指南");
            //创建一个弱引用,让这个弱引用引用到“Struts2权威指南”字符串
            WeakReference weakReference = new WeakReference(str);
            //切断str引用和“Struts2权威指南”字符串之间的引用
            str = null;
            //取出弱引用所引用的对象
            System.out.println(weakReference.get());
            //强制进行垃圾回收
            System.gc();
            System.runFinalization();
            //再次取出弱引用所引用的对象
            System.out.println(weakReference.get());
        }
    }
    
    //执行的结果为:“Struts2权威指南”和null
    弱引用代码
  3. 由上面的代码我们可以看到,程序首先创建了一个“Struts2权威指南”对象,并且让str引用变量引用它。 WeakReference weakReference = new WeakReference(str); 时,系统首先创建了一个弱引用,和str指向同一个对应,当程序执行str = null;的时候,程序剪断了str和“Struts2权威指南”之间的联系,此时“Struts2权威指南”这时只有一个弱引用weakReference指向它,这个时候我们的程序依然可以通过这个弱引用来访问该字符串常量,程序的第一个System.out.println(weakReference.get())依然是可以输出“Struts2权威指南”的,接下来程序强制垃圾回收,如果系统垃圾回收器启动,那么将只有弱引用所引用的对象会被清除掉,当程序执行到第二个System.out.println(weakReference.get())的时候,通常输出的就只是null值了。

  4. 总结: 当我们将str赋值为空,该对象就可以被垃圾回收器回收。因为该对象此时不再含有其他强引用,即使指向该对象的弱引用WeakReference也没有办法阻止垃圾回收器对该对象的回收。相反的,如果该对象还有软引用,str不会被立即回收,除非JVM需要内存。

虚引用 (PhantomReference)

  1. 虚引用通过PhantomReference类实现,虚引用完全类似于没有引用。虚引用对对象本身没有太大的影响,对象甚至感觉不到虚引用的存在。如果一个对象只有一个虚引用时。那它和没有引用的效果大致相同。虚引用主要用于跟踪对象被垃圾回收的状态,虚引用不能单独使用,虚引用必须和引用队列(ReferenceQueue)联合使用。

    public class Main2 {
        public static void main(String[] args) {
               //创建一个字符串引用
            String str = new String("Struts2权威指南");
            //创建一个引用队列
            ReferenceQueue referenceQueue = new ReferenceQueue();
            //创建一个虚引用 让次虚引用引用到“Struts2权威指南”字符串
            PhantomReference phantomReference = new PhantomReference(str, referenceQueue);
            //切断str和“Struts2权威指南”之间的引用关系
            str = null;
            //取出虚引用所引用的对象,并不能通过虚引用访问被引用的对象,
            //所以此处输出的应该是null
            System.out.println(phantomReference.get());
            //强制进行垃圾回收
            System.gc();
            System.runFinalization();
            //取出引用队列最先进入队列中的引用于phantomReference进行比较
            System.out.println(referenceQueue.poll() == phantomReference);
        }
    }
    
    输出结果: nulltrue
    虚引用案例
  2. 因为系统无法通过虚引用来获取被引用的对象,所以在执行第一个System.out.println(phantomReference.get())的时候,打印出来的是空值(即使此时系统并未进行强制垃圾回收)。当程序强制垃圾回收后,只有虚引用引用的字符串对象将会被垃圾回收,当被引用的对象被回收后,对象的引用将被添加到关联的引用队列中去(也就时说,虚引用指向的字符串对象会被垃圾回收器回收,而自己本身将被添加到与之关联的引用队列中去),因此我们才在第二个System.out.println(referenceQueue.poll() == phantomReference)中看到输出的true
  3.  虚引用当垃圾回收器准备回收一个对象时,如果发现它还有虚引用,就会在回收对象的内存之前,把这个虚引用加入到与之关联的引用队列中。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。由于Object.finalize()方法的不安全性、低效性,常常使用虚引用完成对象回收前的资源释放工作。参考我的另一篇博客:解释为什么finalize是不安全的,不建议使用 

    当GC一但发现了虚引用对象,将会将PhantomReference对象插入ReferenceQueue队列. * 而此时PhantomReference所指向的对象并没有被GC回收,而是要等到ReferenceQueue被你真正的处理后才会被回收.

引用队列(ReferenceQueue)(https://blog.csdn.net/cl534854121/article/details/80518070)

  1. 引用队列由java.lang.ref.ReferenceQueue类来表示,它用于保存被回收后对象的引用。当把软引用、弱引用和引用队列联合使用的时候,系统在回收被引用的对象之后,将把被回收对象对应的应用添加到关联的引用队列中。与软引用和弱引用不同的是,虚引用在对象被释放之后,将把已经回收对象对应的虚引用添加它的关联引用队列中,这是得可以在对象被回收之前采取行动。
  2. 软引用和弱引用可以单独使用,但是虚引用不能单独使用,单独使用虚引用没有太大的意义,它必须和引用队列一块使用。程序可以通过判断引用队列中是否已经加入了虚引用,来了解被引用的对象是否将要被垃圾回收。如果程序发现某个虚引用已经被加入到引用队列,那么就可以在所引用的对象的内存被回收之前采取必要的行动。由于Object.finalize()方法的不安全性、低效性,常常使用虚引用完成对象回收前的资源释放工作。参考博客:解释为什么finalize是不安全的,不建议使用

总结

  1. 在应用程序中,我们使用引用类可以边面在程序执行期间将对象留在内存中。如果我们一软引用,弱引用或虚引用的方式引用对象,这样垃圾收集器就能够随意的释放对象。如果希望尽可能的减少程序在其生命周期中所占的内存大小时,这些引用类就很有好处。
  2. 当然必须指出的一个问题: 要使用这些特殊的引用类,就不能保留对对象的强引用。如果保留了对对象的强引用,那么就会浪费这些类所提供的任何好处。
  3. 其实别的引用也可以在new的时候添加引用队列,只是弱引用和软引用是在应用对象gc之后添加到引用队列,而虚引用添加引用队列是在gc之前
  4. 四种引用最主要的区别就是垃圾回收器回收的时机不同: 
    1. 强引用: 我们经常使用的一种引用。基本上垃圾回收器不会主动的去回收
    2. 软引用: 在JVM没有出现内存不足的情况下,垃圾回收器不会去主动回收软引用
    3. 弱引用: 垃圾回收器会立刻回收弱引用。
    4. 虚引用: 虚引用引用的字符串会被垃圾回收器回收, 自己本身会被添加到关联的引用队列中去。

 

posted on 2018-07-10 10:48  CodeZLei  阅读(145)  评论(0编辑  收藏  举报