基于本文回答

播面 播面

文图音视,全方位拆解八股文
0
评论

Java四种引用类型详解

知识点图片

本文讲解Java强、软、弱、虚四种引用。其核心区别是:引用强度依次减弱,直接决定了对象被垃圾回收(GC)的不同时机,分别适用于常规对象、内存敏感缓存、以及特定场景的资源释放跟踪。

我们来详细、清晰地区分Java中的四种引用类型:强引用(Strong Reference)、软引用(Soft Reference)、弱引用(Weak Reference)和虚引用(Phantom Reference)。

这四种引用的主要区别在于它们对Java垃圾回收(Garbage Collection, GC)的影响程度,或者说,它们所指向的对象被GC回收的“时机”不同。引用的强度依次减弱:

强引用 > 软引用 > 弱引用 > 虚引用


1. 强引用 (Strong Reference)

这是我们日常编程中最常使用、也是默认的引用类型。

  • 特点

    • 只要一个对象有强引用指向它,垃圾回收器就绝对不会回收这个对象,无论系统内存多么紧张。
    • 如果内存不足,Java虚拟机会抛出 OutOfMemoryError 错误,使程序异常终止,也不会回收具有强引用的对象。
  • 生命周期

    • 当一个对象没有任何强引用指向它时,它才可能被后续的GC回收。
  • 代码示例

    java
    // new Object() 创建了一个对象,obj 就是对这个对象的强引用
    Object obj = new Object();
    
    // 只有当 obj 不再指向这个对象时,比如:
    obj = null; // 手动断开引用
    // 或者 obj 的作用域结束时,这个对象才可能被GC回收
  • 比喻
    强引用就像你与一个物品之间的“生命纽带”。只要这根纽带存在,谁也无法拿走这个物品。想丢掉它,必须先把纽带剪断 (obj = null)。


2. 软引用 (Soft Reference)

软引用用来描述一些还有用但并非必需的对象。

  • 特点

    • 当一个对象只有软引用指向它时,它会在内存即将不足(即将发生 OutOfMemoryError)之前被垃圾回收器回收。
    • 如果GC回收了这些对象之后,内存仍然不足,才会抛出 OutOfMemoryError
  • 生命周期

    • 比强引用弱。只要内存充足,它就不会被回收;内存不足时,它就会被回收。
  • 主要用途

    • 实现内存敏感的高速缓存。例如,网页缓存、图片缓存等。
    • 缓存的逻辑是:如果内存足够,就保留缓存,加快访问速度;如果内存不足,就清掉这些缓存,以保证程序核心功能的运行,避免程序崩溃。
  • 代码示例

    java
    import java.lang.ref.SoftReference;
    
    // 创建一个强引用的对象
    String strongRef = new String("hello");
    
    // 创建一个指向该对象的软引用
    SoftReference<String> softRef = new SoftReference<>(strongRef);
    
    // 断开强引用,现在只有软引用指向这个 "hello" 字符串对象了
    strongRef = null;
    
    // 当内存充足时,可以通过 get() 方法获取到对象
    System.out.println(softRef.get()); // 输出 "hello"
    
    // 当系统内存紧张时,GC会回收软引用对象
    // 此时再次调用 get() 方法会返回 null
    // System.gc(); // 强制GC不一定会回收软引用,取决于JVM内存情况
  • 比喻
    软引用就像你家里的“闲置物品”。屋子(内存)宽敞时,你就留着它。当有更重要的东西要放进来,屋子不够大了,你就会先把这些闲置物品扔掉,腾出空间。


3. 弱引用 (Weak Reference)

弱引用的强度比软引用更弱,它描述的是非必需的对象。

  • 特点

    • 当一个对象只有弱引用指向它时,无论当前内存是否充足,只要垃圾回收器运行,这个对象就一定会被回收
  • 生命周期

    • 只能“存活”到下一次GC发生之前。
  • 主要用途

    • WeakHashMap 是一个典型的应用。WeakHashMap 的Key是弱引用。当一个Key对象没有其他强引用指向它时,GC会回收它,WeakHashMap 也会自动移除对应的键值对。
    • 防止内存泄漏,特别是在监听器和回调函数的场景中。如果一个对象注册了监听器,但之后不再使用,如果没有妥善处理,监听器会持有该对象的引用,导致其无法被回收。使用弱引用可以解决这个问题。
  • 代码示例

    java
    import java.lang.ref.WeakReference;
    
    String strongRef = new String("hello");
    WeakReference<String> weakRef = new WeakReference<>(strongRef);
    
    // 断开强引用
    strongRef = null;
    
    System.out.println(weakRef.get()); // 可能输出 "hello"
    
    // 强制进行垃圾回收
    System.gc();
    
    // GC之后,弱引用所指向的对象很大概率已经被回收
    System.out.println(weakRef.get()); // 输出 null
  • 比喻
    弱引用就像贴在布告栏上的“广告传单”。清洁工(GC)每次来打扫卫生时,不管布告栏是否贴满,都会把这些传单清理掉。


4. 虚引用 (Phantom Reference)

也称为“幽灵引用”或“幻影引用”,是所有引用类型中最弱的。

  • 特点

    • 一个对象是否有虚引用,完全不影响其生命周期。它就和没有任何引用一样,在任何时候都可能被GC回收。
    • 无法通过虚引用来获取对象实例PhantomReferenceget() 方法永远返回 null
    • 它的唯一作用是:在这个对象被垃圾回收器回收时,能收到一个系统通知。
  • 使用要求

    • 虚引用必须和引用队列(ReferenceQueue)联合使用。当GC准备回收一个对象时,如果发现它还有虚引用,就会在回收该对象之前,把这个虚引用加入到与之关联的引用队列中。
  • 主要用途

    • 管理堆外内存(Direct Memory)。例如,NIO 中的 DirectByteBuffer。当 DirectByteBuffer 对象被回收时,可以通过虚引用机制,让一个专门的线程从引用队列中获取通知,然后调用本地方法释放对应的堆外内存,从而避免内存泄漏。
  • 代码示例

    java
    import java.lang.ref.PhantomReference;
    import java.lang.ref.ReferenceQueue;
    
    ReferenceQueue<Object> queue = new ReferenceQueue<>();
    Object obj = new Object();
    PhantomReference<Object> phantomRef = new PhantomReference<>(obj, queue);
    
    // get() 方法永远返回 null
    System.out.println(phantomRef.get()); // 输出 null
    
    // 断开强引用
    obj = null;
    
    // 在某个时刻GC运行时,obj被回收,phantomRef会被加入到queue中
    System.gc();
    
    // 在未来的某个时间点,可以从队列中取出虚引用,说明其指向的对象已经被回收
    // 实际应用中会有一个专门的线程来轮询这个队列
    // if (queue.poll() != null) {
    //     System.out.println("对象已被GC回收,可以执行清理操作了");
    // }
  • 比喻
    虚引用就像一份“死亡通知书”。你无法用它来“复活”死者(获取对象),它的唯一作用就是告诉你“这个人已经去世了,你可以处理后事了”(对象已被回收,你可以清理相关联的外部资源了)。


总结对比

引用类型 回收时机 强度 主要用途 get()方法
强引用 (Strong) 从不回收(除非无引用) 最强 普通的对象引用 返回对象
软引用 (Soft) 内存不足时回收 较强 内存敏感的缓存 可能返回 null
弱引用 (Weak) 下一次GC时回收 较弱 WeakHashMap、防止内存泄漏 可能返回 null
虚引用 (Phantom) 和没有引用一样,随时可回收 最弱 管理堆外内存、跟踪对象回收 总是返回 null
00:00
00:00