为什么 Java 有 4 种不同类型的引用?

问题描述

直到今天,我才意识到 Java 有四种主要类型的引用。

  1. 强引用Java 使用的认引用类型。
  2. 弱引用:如果一个对象有一个弱引用,那么即使有足够的内存,GC 也会在下次运行时回收这个对象的内存。
  3. 软引用:如果一个对象有一个软引用,那么 GC 只会在它非常需要一些内存时才回收这个对象的内存。
  4. 幻像引用:如果一个对象有一个幻像引用,那么它就有资格进行垃圾回收。但是,在 GC 之前,JVM 将应该被垃圾回收的对象放入名为 reference queue 的队列中。

我理解了基本概念,并编写了一个小程序来了解每种引用类型的工作原理。

import java.lang.ref.Phantomreference;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.softReference;
import java.lang.ref.WeakReference;

class User 
{ 
    public User info() 
    { 
        System.out.println("Info method invoked from User class");
        return null;
    }   
} 

public class ReferencesExample 
{ 
    public static void main(String[] args) 
    { 
        //Strong Reference
        User userRefObj = new User(); 
        System.out.println("1 :" + userRefObj.info());

        // Weak Reference
        WeakReference<User> weakref = new WeakReference<User>(userRefObj.info());  
        System.out.println("2 : " + weakref);

        // Soft Reference    
        SoftReference<User> softref = new SoftReference<User>(userRefObj.info());  
        System.out.println("3 : " + softref);

        // Phantom Reference 
        ReferenceQueue<User> refQueueObj = new ReferenceQueue<User>(); 
        Phantomreference<User> phantomref = new Phantomreference<User>(userRefObj.info(),refQueueObj); 
        System.out.println("4 : " + phantomref);     
       
    } 
}

输出

1 :null
Info method invoked from User class
Info method invoked from User class
2 : java.lang.ref.WeakReference@15db9742
Info method invoked from User class
3 : java.lang.ref.softReference@6d06d69c
Info method invoked from User class
4 : java.lang.ref.Phantomreference@7852e922

疑问:我们如何决定在实际场景中使用哪种引用类型以及在何处使用?

解决方法

事实的答案非常简单:因为仅靠强引用是不够的。在现实世界的生产代码中,您肯定需要一些弱引用的概念。

然后可以争辩说其他类型只是过度工程化。该语言的设计者认为这些是有帮助的,但除了非常模糊的情况外,它们并非如此。但注意:对应的语义无法用普通的java代码来完成。所以这是其中一种情况“让我们将它添加到语言中,以防我们需要它”。

但第二段很容易被视为意见,而不是事实。

,

这是一个复杂的答案,尤其是“现实世界”部分。这些特殊的参考文献是一个(非常)锋利的工具,应该很少接触并且有很好的理解。您的第一个误解是只有 Phantom References 使用 queue(称为 ReferenceQueue)。事实上,他们使用queue。其中,PhantomReference 有点“怪人”,主要是因为围绕它的规范已经从 java-8 变成了 java-9。您可以阅读 this answer to get some details(目前尚不清楚在 java-8 中保持实例活动的原因)。

然后您得到的常见误解是“下一次运行”,有关更多详细信息,您可以take a look here。这里的“第二个”或“下一个”的概念有点棘手;但关键是这是不确定的。

提供“真实世界”的例子也有点棘手。发生这种情况是因为您很少直接使用这些引用,它们通常预先打包在实用程序中。例如,您可以使用 take a look of how WeakHashMap 工作(直接在引擎盖下使用 WeakReferenceReferenceQueue)或事件 this answerguava 及其 CacheBuilder 用于提供一些处理 SoftReferences(但不再是)、caffeine still does though 的功能。

PhantomReferences 在内部用于 Cleaner API(自 java-9)。 here 是显示其一般用法的示例。没有 PhantomReferences,这真的不可能(至少不是那么“容易”)。


就此而言,我们在生产中使用 WeakHashMap(我们也使用 Cleaner API,但要少得多)。如果您真的想了解它们,您需要了解 garbage collector 的工作原理(例如,真的 工作),然后阅读他们的官方文档并尝试(并询问)多种方法。