• 【JDK1.8】java.lang.ref 包


    java.lang.ref 包

    【JDK1.8案例源码】

    Java.lang.ref 是 Java 类库中比较特殊的一个包,它提供了与 Java 垃圾回收器密切相关的引用类。

    reference objects are implemented in close cooperation with the garbage collector

    包内结构

    java.lang.ref 包

    类图

    java.lang.ref 包

    Reference 是一个抽象类,而 SoftReference,WeakReference,PhantomReference 以及 FinalReference 都是继承它的具体类。

    Reference 特性及用法

    StrongReference

    我们都知道 JVM 中对象是被分配在堆(heap)上的,当程序行动中不再有引用指向这个对象时,
    这个对象就可以被垃圾回收器所回收。这里所说的引用也就是我们一般意义上申明的对象类型的变量(如 String, Object, ArrayList 等),
    区别于原始数据类型的变量(如 int, short, long 等)也称为强引用。在了解虚引用之前,我们一般都是使用强引用来对对象进行引用。如:

    清单 1. StrongReference usage

    
    String tag = new String("T");
    
    

    此处的 tag 引用就称之为强引用。而强引用有以下特征:

    • 强引用可以直接访问目标对象。
    • 强引用所指向的对象在任何时候都不会被系统回收。
    • 强引用可能导致内存泄漏。

    我们要讨论的这三种 Reference 较之于强引用而言都属于”弱引用”,也就是他们所引用的对象只要没有强引用,
    就会根据条件被 JVM 的垃圾回收器所回收,它们被回收的时机以及用法各不相同。下面分别来进行讨论。

    SoftReference

    SoftReference 在”弱引用”中属于最强的引用。SoftReference 所指向的对象,当没有强引用指向它时,会在内存中停留一段的时间,
    垃圾回收器会根据 JVM 内存的使用情况(内存的紧缺程度)以及 SoftReference 的 get() 方法的调用情况来决定是否对其进行回收。

    具体使用一般是通过 SoftReference 的构造方法,将需要用弱引用来指向的对象包装起来。
    当需要使用的时候,调用 SoftReference 的 get() 方法来获取。
    当对象未被回收时 SoftReference 的 get() 方法会返回该对象的强引用。如下:

    清单 2. SoftReference usage

    
    SoftReference<Bean> bean = new SoftReference<Bean>(new Bean("name", 10));
    System.out.println(bean.get());// "name:10”
    
    

    软引用有以下特征:

    • 软引用使用 get() 方法取得对象的强引用从而访问目标对象。
    • 软引用所指向的对象按照 JVM 的使用情况(Heap 内存是否临近阈值)来决定是否回收。
    • 软引用可以避免 Heap 内存不足所导致的异常。

    当垃圾回收器决定对其回收时,会先清空它的 SoftReference,也就是说 SoftReference 的 get() 方法将会返回 null,
    然后再调用对象的 finalize() 方法,并在下一轮 GC 中对其真正进行回收。

    WeakReference

    WeakReference 是弱于 SoftReference 的引用类型。弱引用的特性和基本与软引用相似,
    区别就在于弱引用所指向的对象只要进行系统垃圾回收,不管内存使用情况如何,永远对其进行回收(get() 方法返回 null)。
    完全可以通过和 SoftReference 一样的方式来操作 WeakReference,这里就不再复述。

    弱引用有以下特征:

    • 弱引用使用 get() 方法取得对象的强引用从而访问目标对象。
    • 一旦系统内存回收,无论内存是否紧张,弱引用指向的对象都会被回收。
    • 弱引用也可以避免 Heap 内存不足所导致的异常。

    PhantomReference

    PhantomReference 是所有”弱引用”中最弱的引用类型。不同于软引用和弱引用,
    虚引用无法通过 get() 方法来取得目标对象的强引用从而使用目标对象,观察源码可以发现 get() 被重写为永远返回 null。

    那虚引用到底有什么作用?其实虚引用主要被用来 跟踪对象被垃圾回收的状态 ,
    通过查看引用队列中是否包含对象所对应的虚引用来判断它是否 即将 被垃圾回收,从而采取行动。
    它并不被期待用来取得目标对象的引用,而目标对象被回收前,它的引用会被放入一个 ReferenceQueue 对象中,从而达到跟踪对象垃圾回收的作用。

    所以具体用法和之前两个有所不同,它必须传入一个 ReferenceQueue 对象。当虚引用所引用对象被垃圾回收后,虚引用会被添加到这个队列中。如:

    清单 3. PhantomReference usage

    
        public static void main(String[] args) {
            ReferenceQueue<String> refQueue = new ReferenceQueue<String>();
            PhantomReference<String> referent = new PhantomReference<String>(new String("T"), refQueue);
            System.out.println(referent.get());// null
    
            System.gc();
            System.runFinalization();
    
            System.out.println(refQueue.poll() == referent); //true
        }
        
    

    值得注意的是,对于引用回收方面,虚引用类似强引用不会自动根据内存情况自动对目标对象回收,
    Client 需要自己对其进行处理以防 Heap 内存不足异常。

    虚引用有以下特征:

    • 虚引用永远无法使用 get() 方法取得对象的强引用从而访问目标对象。
    • 虚引用所指向的对象在被系统内存回收前,虚引用自身会被放入 ReferenceQueue 对象中从而跟踪对象垃圾回收。
    • 虚引用不会根据内存情况自动回收目标对象。

    另外值得注意的是,其实 SoftReference, WeakReference 以及 PhantomReference 的构造函数都可以接收一个 ReferenceQueue 对象。
    当 SoftReference 以及 WeakReference 被清空的同时,也就是 Java 垃圾回收器准备对它们所指向的对象进行回收时,调用对象的 finalize() 方法之前,
    它们自身会被加入到这个 ReferenceQueue 对象 中,此时可以通过 ReferenceQueue 的 poll() 方法取到它们。
    而 PhantomReference 只有当 Java 垃圾回收器对其所指向的对象真正进行回收时,会将其加入到这个 ReferenceQueue 对象 中,这样就可以追综对象的销毁情况。

    各种引用类型总结

    表 1. 引用类型特性总结

    引用类型 取得目标对象方式 垃圾回收条件 是否可能内存泄漏
    强引用 直接调用 不回收 可能
    软引用 通过 get() 方法 视内存情况回收 可能
    弱引用 通过 get() 方法 永远回收 可能
    虚引用 无法取得 不回收 可能
    注意:
        如果想使用这些相对强引用来说较弱的引用来进行对象操作的时候,
        就必须保证没有强引用指向被操作对象。否则将会被视为强引用指向,
        不会具有任何的弱引用的特性。
    

    表 2:各个引用在 GC 后是否被回收?

    引用类型 GC 后是否回收
    强引用 不回收
    软引用 视内存情况回收
    弱引用 永远回收
    虚引用 不回收

    表 3:各个引用创建大量对象时是否导致 Heap 不足异常?

    引用类型 是否抛出异常
    强引用 直接调用
    软引用 通过 get() 方法
    弱引用 通过 get() 方法
    虚引用 无法取得

    FinalReference 以及 Finzlizer

    FinalReference 作为 java.lang.ref 里的一个不能被公开访问的类,
    又起到了一个什么样的作用呢?作为他的子类,
    Finalizer 又在垃圾回收机制里扮演了怎么样的角色呢?
    

    实际上,FinalReference 代表的正是 Java 中的强引用,如这样的代码 :

    Bean bean = new Bean();
    

    在虚拟机的实现过程中,实际采用了 FinalReference 类对其进行引用。而 Finalizer,除了作为一个实现类外,
    更是在虚拟机中实现一个 FinalizerThread,以使虚拟机能够在所有的强引用被解除后实现内存清理。

    让我们来看看 Finalizer 是如何工作的。首先,通过声明 FinalizerThread,并将该线程实例化,设置为守护线程后,加入系统线程中去。

    在 GC 的过程中,当一个强引用被释放,由系统垃圾收集器标记后的对象,会被加入 Finalizer 对象中的 ReferenceQueue 中去,
    并调用 Finalizer.runFinalizer() 来执行对象的 finalize 方法。

    注意,
    标记处所调用的 invokeFinalizeMethod 为 native 方法,由于 finalize 方法在 Object 类中被声明为 protected,
    这里必须采用 native 方法才能调用。随后通过将本地强引用设置为空,以便使垃圾回收器清理内存。

    可以看到,通过这样的方法,Java 将四种引用对象类型:
    软引用 (SoftReference),弱引用 (WeakReference),强引用 (FinalReference),虚引用 (PhantomReference) 平等地对待,
    并在垃圾回收器中进行统一调度和管理。

    回顾

    • 软引用 (SoftReference)
      引用类型表现为当内存接近满负荷 , 或对象由 SoftReference.get() 方法的调用没有发生一段时间后 , 垃圾回收器将会清理该对象 . 在运行对象的 finalize 方法前 , 会将软引用对象加入 ReferenceQueue 中去 .
    • 弱引用 (WeakReference)
      引用类型表现为当系统垃圾回收器开始回收时 , 则立即会回收该对象的引用 . 与软引用一样 , 弱引用也会在运行对象的 finalize 方法之前将弱引用对象加入 ReferenceQueue.
    • 强引用 (FinalReference)
      这是最常用的引用类型 . JVM 系统采用 Finalizer 来管理每个强引用对象 , 并将其被标记要清理时加入 ReferenceQueue, 并逐一调用该对象的 finalize() 方法 .
    • 虚引用 (PhantomReference)
      这是一个最虚幻的引用类型 . 无论是从哪里都无法再次返回被虚引用所引用的对象 . 虚引用在系统垃圾回收器开始回收对象时 , 将直接调用 finalize() 方法 , 但不会立即将其加入回收队列 . 只有在真正对象被 GC 清除时 , 才会将其加入 Reference 队列中去 .

    参考

  • 相关阅读:
    OI中的小智慧
    洛谷 P2335 SDOI 2005 毒瘤 位图(也补上注释了)
    洛谷P4779 Dijkstra 模板
    洛谷 P1156 垃圾陷阱 谈论剪枝,非满分
    8/14考试 JWG
    一个好消息 JWG
    刷水题(一) JWG
    C语言运算符优先级从没像现在这样深刻体会
    cron 备忘
    CentOS
  • 原文地址:https://www.cnblogs.com/ljmatlight/p/13505511.html
Copyright © 2020-2023  润新知