Java基础- Java 中的引用类型

基本概念

Java 中的引用类型主要分为强引用、软引用、弱引用、虚引用和终结器引用,它们在垃圾回收(GC)过程中表现不同,从而提供了不同程度的内存管理灵活性。

1. 强引用(Strong Reference)

  • 定义:最常见的引用类型,如 Object obj = new Object()。只要强引用还存在,垃圾回收器永远不会回收掉被引用的对象。
  • 特点
    • 如果一个对象具有强引用,它将保持在内存中。
    • 只有当所有指向它的强引用都不再使用时,对象才可能被垃圾回收。

2. 软引用(Soft Reference)

  • 定义:用于描述一些有用但非必需的对象。在 java.lang.ref 包中通过 SoftReference 类实现。
  • 特点
    • 当 JVM 认为内存不足时,会回收这些对象。
    • 适用于实现内存敏感的缓存。
    • 如果垃圾回收后内存仍然不足,那么软引用的对象将被回收。

3. 弱引用(Weak Reference)

  • 定义:比软引用更弱一些,在下一次垃圾回收发生时,无论内存是否足够,都会回收其引用的对象。通过 WeakReference 类实现。
  • 特点
    • 通常用于实现规范映射(Canonicalizing Mappings)。
    • 一旦垃圾收集器扫描到弱引用,不管当前内存空间足够与否,都会回收其对象。

4. 虚引用(Phantom Reference)

  • 定义:最弱的一种引用类型。通过 PhantomReference 类实现,并且必须和引用队列(ReferenceQueue)联合使用。
  • 特点
    • 虚引用的对象在被回收时,其虚引用会被加入到与之关联的引用队列中。
    • 常用于管理堆外内存(如直接内存),以及精确的资源释放。

5. 终结器引用(Finalizer Reference)

  • 定义:当对象变得不可达时,如果对象重写了 finalize 方法,它被加入到一个由垃圾回收器维护的特殊队列中,这是通过终结器引用实现的。
  • 特点
    • 它允许对象在被垃圾回收前执行一些清理操作。
    • 由于 finalize 方法的不可预测性和性能问题,其使用通常不推荐。
    • 在 Java 9 中被弃用。

使用注意事项

  • 强引用是常规程序的默认行为。
  • 软引用和弱引用可以用于实现缓存。
  • 虚引用和终结器引用用于精确的资源管理和清理,但它们的使用需要非常小心,因为不当的使用可能导致内存泄漏或性能问题。
  • 从 Java 9 开始,由于 finalize 方法的缺点,建议使用 Cleaner 或其他替代方案来替代终结器引用。

强引用

强引用(Strong Reference)是 Java 编程语言中最常见的引用类型,也是默认的引用形式。在 Java 的内存管理和垃圾回收(GC)机制中,强引用扮演着重要的角色。

定义和特性

  1. 定义:当在代码中创建一个对象并将其赋值给一个引用变量时,这个引用就是强引用。例如:

    Object obj = new Object();
    

    在这个例子中,obj 是一个指向 Object 实例的强引用。

  2. 内存管理:只要对象至少被一个强引用所持有,垃圾回收器就永远不会回收它。因此,强引用防止了其所指向的对象被垃圾回收器回收。

  3. 内存泄漏风险:如果强引用被过度使用或不当管理,可能导致内存泄漏。当不再需要某个对象时,应该将其对应的强引用设为 null,以便垃圾回收器能够回收该对象:

    obj = null; // 对象现在可以被垃圾回收
    
  4. 生命周期:强引用的对象具有较长的生命周期,它们只在引用被显式地移除或变量离开作用域时才变得可回收。

使用场景

强引用适用于绝大多数的正常使用场景,其中对象在其生命周期内被频繁访问。例如:

  • 局部变量和成员变量通常是强引用。
  • 数据结构中的元素(如数组、链表、树、图中的节点)通常被作为强引用处理。

注意事项

  • 管理强引用需要谨慎,特别是在创建大型对象或有长生命周期的对象时。
  • 在对象不再需要时,应该及时清除对它的强引用,以免占用不必要的内存。
  • 调试内存泄漏时,寻找未被释放的强引用是一个常见的做法。

总的来说,强引用是 Java 中最直接、最简单的引用类型。它们在对象生命周期管理中发挥核心作用,但也需要妥善管理以避免内存泄漏和其他内存相关的问题。

软引用

软引用(Soft Reference)在 Java 中是一种特殊的引用类型,提供了一种内存敏感的缓存机制。通过 java.lang.ref.SoftReference 类实现,软引用允许对象在内存足够时保持活动状态,而在内存不足时被垃圾回收器回收。

定义和特性

  1. 创建软引用

    • 使用 SoftReference 类创建软引用。例如:
      SoftReference<Object> softRef = new SoftReference<>(new Object());
      
    • 这里,一个新的 Object 被创建,并且其软引用被赋值给 softRef
  2. 内存管理

    • 当 JVM 需要回收内存时,如果发现只通过软引用引用的对象,则这些对象可能被垃圾回收。
    • 相较于强引用,软引用降低了对象的生存强度,使得它们在内存紧张时成为可回收的候选。
  3. 用途

    • 软引用通常用于实现内存敏感的缓存。
    • 当系统内存充足时,软引用可以帮助提高程序的性能;当内存不足时,软引用的对象可以被自动回收,从而有效地减少了内存溢出(OutOfMemoryError)的风险。
  4. 回收策略

    • 垃圾回收器在决定是否回收软引用对象时,会考虑到当前的堆内存使用情况。如果内存足够,软引用对象通常不会被回收。
    • 在内存不足时,所有的软引用对象都可能被回收。

示例代码

Object obj = new Object();
SoftReference<Object> softRef = new SoftReference<>(obj);

// 使用软引用指向的对象
Object dereferencedObj = softRef.get();
if (dereferencedObj != null) {
    // 对象还没有被回收,使用它
} else {
    // 对象被回收了,需要重新创建或采取其他措施
}

注意事项

  • 软引用非常适合实现缓存,因为它们在内存足够时可以保留缓存项,而在内存不足时自动清理。
  • 使用软引用时需要考虑到垃圾回收器的行为可能会因不同的 JVM 实现和配置而异。
  • 在设计缓存或其他使用软引用的系统时,应该考虑到可能的性能影响,尤其是在内存紧张时的性能表现。

总体而言,软引用提供了一种灵活的内存管理方式,允许应用程序在保持高性能的同时减少内存溢出的风险。然而,合理使用软引用需要对应用程序的内存需求和垃圾回收机制有充分的理解。

弱引用

弱引用(Weak Reference)在 Java 中是一种特别的引用类型,允许对象在没有其他强引用存在的情况下被垃圾回收器回收。它们是通过 java.lang.ref.WeakReference 类实现的,并在某些具体应用场景中非常有用。

定义和特性

  1. 创建弱引用

    • 弱引用是通过创建 WeakReference 对象来实现的。例如:
      WeakReference<Object> weakRef = new WeakReference<>(new Object());
      
    • 在这个例子中,一个新的 Object 被创建,并且其弱引用被赋值给 weakRef
  2. 垃圾回收行为

    • 弱引用的一个关键特点是一旦垃圾回收器工作,如果一个对象只被弱引用指向,那么这个对象几乎肯定会被回收。
    • 这与软引用不同,软引用的对象在内存不足时才会被回收。
  3. 用途

    • 弱引用通常用于实现规范映射(Canonicalizing Mappings)和监听器列表,以及其他需要在对象不再被使用时自动释放它们的场景。
    • java.util.WeakHashMap 是使用弱引用的典型例子。在这种映射中,一旦键不再被其他对象强引用,该键和其对应的值就可以被自动回收。
  4. 弱引用与缓存

    • 弱引用不适合用于实现缓存,因为对象可能会在任何时间被回收,这使得缓存的内容不可预测。

示例代码

Object obj = new Object();
WeakReference<Object> weakRef = new WeakReference<>(obj);

// 在这一点上,obj 不再有强引用指向
obj = null;

// 使用弱引用指向的对象
Object dereferencedObj = weakRef.get();
if (dereferencedObj != null) {
    // 对象还没有被回收,使用它
} else {
    // 对象已被回收
}

注意事项

  • 弱引用允许垃圾回收器更加灵活地回收对象,这对于避免内存泄漏特别有帮助。
  • 它们在实现一些与资源释放相关的数据结构时非常有用,如 WeakHashMap
  • 弱引用的一个常见用途是在引用对象时不妨碍其被垃圾回收,这对于监听器和其他回调非常重要,因为它们可以在不被需要时自动被清理,避免了内存泄漏。

总而言之,弱引用在 Java 中提供了一种机制,使得对象可以在不再被需要时自动被清理,从而有助于内存管理和防止内存泄漏。然而,由于它们的不可预测性,弱引用需要在合适的场景中谨慎使用。

虚引用

虚引用(Phantom Reference)在 Java 中是最弱的一种引用类型。与软引用和弱引用不同,虚引用不影响其引用的对象的生命周期。当垃圾回收器决定回收一个对象,如果这个对象与虚引用关联,垃圾回收器会把这个虚引用加入到一个引用队列(ReferenceQueue)。通过检查这个队列,程序可以知道某个特定对象的回收状态。

定义和特性

  1. 创建虚引用

    • 虚引用是通过 java.lang.ref.PhantomReference 类创建的。它必须与 ReferenceQueue 一起使用。例如:
      ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
      PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), refQueue);
      
    • 在这里,一个新的 Object 被创建,并且其虚引用被赋值给 phantomRef
  2. 垃圾回收行为

    • 当垃圾回收器准备回收一个对象时,如果发现它有虚引用,就会在回收对象之前将该虚引用加入到与之关联的 ReferenceQueue
    • 通过检查这个队列,应用程序可以知道这个对象即将被回收。
  3. 用途

    • 虚引用主要用于确切地知道对象何时被从内存中移除,并进行特定的清理工作。
    • 一个典型的应用是管理直接缓冲区(Direct Buffer)的内存,比如 NIO 中的 ByteBuffer,这种情况下 Java 垃圾回收器不能完全控制内存回收。
  4. 不影响对象生命周期

    • 虚引用不会延长其引用的对象的生命周期。当垃圾回收器决定回收一个对象时,是否存在虚引用不会发挥作用。

示例代码

ReferenceQueue<Object> refQueue = new ReferenceQueue<>();
PhantomReference<Object> phantomRef = new PhantomReference<>(new Object(), refQueue);

// 执行垃圾回收
System.gc();

// 检查引用队列
Reference<?> ref = refQueue.poll();
if (ref != null) {
    // 被引用的对象即将被回收,可以进行必要的清理操作
}

注意事项

  • 虚引用主要用于调度前的资源释放工作,如关闭文件或网络连接等。
  • 使用虚引用时需要谨慎,因为它不保证对象的可达性。
  • 虚引用提供了一种确保对象被垃圾回收时接收到通知的方式,但这种通知机制不能代替正规的资源管理和清理。

虚引用在 Java 中为程序员提供了一种确切知道对象何时被清理出内存的机制。虽然它在日常编程中的应用不像软引用和弱引用那样常见,但在某些特定场景下,比如涉及到资源释放和清理的情况,虚引用是非常有用的。

终结器引用

终结器引用(Finalizer Reference)在 Java 中是指通过对象的 finalize() 方法实现的一种机制,用于在对象被垃圾回收之前执行清理操作。终结器引用主要通过 java.lang.Object 类中定义的 finalize() 方法来实现。

定义和特性

  1. finalize() 方法

    • 每个 Java 对象都继承了 Object 类,因此都继承了 finalize() 方法。这个方法在垃圾回收器决定回收对象之前被调用,允许执行清理操作,如关闭文件或释放资源。
    • 默认情况下,finalize() 方法不执行任何操作,但可以被重写以实现特定的清理逻辑。
  2. 终结器队列

    • 当一个对象变得不可达时,如果它重写了 finalize() 方法,它会被放入一个由垃圾回收器维护的终结器队列。
    • 垃圾回收器将以单独的线程运行,并从队列中取出这些对象,调用它们的 finalize() 方法。
  3. 对象的复活

    • 有趣的是,在 finalize() 方法中,对象可以重新获得引用,这称为对象的复活。如果这种情况发生,垃圾回收器在当前回收周期内不会回收该对象。
  4. 二次回收

    • 如果对象在执行 finalize() 方法后仍然无法达到,则在下一次垃圾回收时,该对象将被回收。

示例代码

public class FinalizerExample {

    @Override
    protected void finalize() throws Throwable {
        try {
            // 清理资源,如关闭文件
        } finally {
            super.finalize();
        }
    }

    // 类的其他部分
}

注意事项

  • 性能问题:使用终结器引用可能导致性能下降。finalize() 方法的调用会延迟对象的回收,增加垃圾回收的复杂性。
  • 不可靠:不保证 finalize() 方法总会被及时调用,也不保证它们的调用顺序。
  • 安全问题:在 finalize() 方法中复活对象可能导致程序行为不稳定和内存泄漏。
  • 已废弃:从 Java 9 开始,finalize() 方法被标记为废弃,因为它是不推荐使用的。

替代方案

由于 finalize() 方法的这些问题,Java 提供了其他机制来替代终结器引用,例如:

  • try-with-resources 语句:用于自动关闭实现了 AutoCloseable 接口的资源。
  • 清理器(Cleaner):在 Java 9 中引入,用于替代 finalize(),提供了更安全和高效的资源清理机制。

总之,尽管终结器引用在某些情况下可以用于执行清理操作,但由于它们的不可预测性和性能问题,一般建议使用其他更可靠的资源管理机制。

你可能感兴趣的:(Java基础,java,jvm)