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 运行(不可确保清理立即执行)
}
}
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 中推荐的资源清理工具,提供了一种高效、可靠的资源管理方式。