Java Cleaner 类

java.lang.ref.Cleaner 是 Java 9 引入的工具,用于管理对象的清理工作,提供了一种在垃圾回收时清理资源的安全方式。它可以代替 finalize 方法,通过在对象不再被使用时执行清理操作来释放外部资源,且比 finalize 更高效、可靠。

1. Cleaner 的作用

Cleaner 允许我们注册一个清理动作(clean-up action),该动作会在对象变得不可达(即即将被垃圾回收)时自动执行。典型的应用场景包括:

  • 自动释放非 Java 内存资源,例如关闭文件、数据库连接、socket 连接等。
  • 取代 finalize,实现资源的自动释放,避免因忘记调用 close() 方法导致的资源泄露。

2. Cleaner 的基本使用

以下是 Cleaner 的基本用法:

  • 创建 Cleaner 实例:Cleaner.create()
  • 创建一个需要清理的对象,并将其与清理动作注册到 Cleaner 中。当该对象不再被引用时,清理动作会自动执行。

3. 使用示例

以下示例展示了如何使用 Cleaner 创建一个需要清理的资源,并在对象不可达时自动执行清理操作:

实例

import java.lang.ref.Cleaner;

public class CleanerExample {

    // 创建一个 Cleaner 实例
    private static final Cleaner cleaner = Cleaner.create();

    // 定义一个需要清理的资源
    static class Resource implements AutoCloseable {
        // 清理任务 - 用于释放资源
        private final Cleaner.Cleanable cleanable;

        Resource() {
            // 注册清理任务,当 Resource 实例变得不可达时执行清理动作
            this.cleanable = cleaner.register(this, new ResourceCleaner());
            System.out.println("Resource initialized");
        }

        // 清理资源
        private static class ResourceCleaner implements Runnable {
            @Override
            public void run() {
                System.out.println("Cleaning up resource...");
            }
        }

        @Override
        public void close() {
            // 主动释放资源,取消 Cleaner 注册的清理任务
            cleanable.clean();
            System.out.println("Resource closed");
        }
    }

    public static void main(String[] args) {
        // 使用资源
        try (Resource resource = new Resource()) {
            System.out.println("Using resource...");
        } // 自动调用 close(),手动清理资源

        // 如果未调用 close(),GC 会执行清理任务
        new Resource(); // 不调用 close(),等待 GC 触发清理
        System.gc(); // 提示 GC 运行(不可确保清理立即执行)
    }
}

4. 代码说明

  • Cleaner 实例Cleaner.create() 创建了一个 Cleaner 实例,用于注册需要清理的资源。
  • 注册清理任务:在 Resource 类中,调用 cleaner.register(this, new ResourceCleaner()) 将当前对象注册到 Cleaner,指定清理任务 ResourceCleaner
  • 自动清理:当 Resource 实例没有其他强引用并即将被垃圾回收时,Cleaner 会自动调用 ResourceCleaner.run(),输出"Cleaning up resource..."。
  • 手动清理:通过 close() 方法主动调用 cleanable.clean(),立即清理资源,并取消 Cleaner 的任务。

5. Cleaner 的特点和优势

  • 高效Cleaner 由 JVM 管理,避免了 finalize 的低效性能问题。
  • 避免 finalize 延迟Cleaner 注册的任务在对象变得不可达后可以及时执行,而不是等待垃圾回收器延迟调用。
  • 可控性Cleaner 允许主动调用 clean() 清理资源,并在清理后自动取消任务,避免重复清理。
  • 内存和资源安全性:减少手动管理资源的风险,确保系统资源的正确释放。

6. Cleaner 的使用场景

Cleaner 适合管理需要自动释放的资源,特别是在无法使用 try-with-resources 时。以下是典型应用场景:

  • Native 资源:用于管理非堆内存资源,比如 DirectByteBuffer、JNI 或者其他本地资源。
  • 缓存清理:当对象被垃圾回收时,自动清理相关缓存数据。
  • 非阻塞资源清理:用于无阻塞的资源清理任务,例如通知其他服务对象已被回收。

7. 注意事项

  • 适度使用Cleaner 主要用于特殊场景,例如确保无泄露的非 Java 资源。对于简单的资源管理,try-with-resources 更加适合。
  • 不可控的执行时间:尽管 Cleaner 的清理任务比 finalize 更加及时,但清理任务的确切执行时间仍然取决于垃圾回收的时间,不适合对时效性要求严格的资源管理。
  • 资源密集型清理任务慎用:避免在 Cleaner 中执行过于复杂或长时间的操作,以免影响垃圾回收性能。

总体而言,java.lang.ref.Cleaner 是 Java 中推荐的资源清理工具,提供了一种高效、可靠的资源管理方式。