使用 java.lang.ref.Cleaner 替代 Object.finalize
Usage of java.lang.ref.Cleaner as alternative to Object.finalize
我需要清理 JNI 调用分配的资源。通过覆盖 Object.finalize()
方法很容易做到这一点。由于从 Java 9 开始不推荐使用此方法,因此我尝试使用新的 java.lang.ref.Cleaner
class.
来实现相同的目的
下面是实例被垃圾回收前调用ToBeCleaned.cleanUp
方法的代码:
import java.lang.ref.Cleaner;
import java.lang.ref.WeakReference;
public class ToBeCleaned {
private static Cleaner cleaner = Cleaner.create();
public ToBeCleaned() {
cleaner.register(this, new CleanRunnable(this));
}
void cleanUp () {
// do cleanup
}
static class CleanRunnable implements Runnable {
// It has to be weak reference, otherwise ToBeCleaned instance
// would never be eligible for GC
private WeakReference<ToBeCleaned> toBeCleanedWeakReference;
CleanRunnable(ToBeCleaned toBeCleaned) {
this.toBeCleanedWeakReference = new WeakReference<>(toBeCleaned);
}
@Override
public void run() {
toBeCleanedWeakReference.get().cleanUp();
}
}
}
我的问题:这是正确的方法吗?
你的方法有缺陷。 “清理操作”不得依赖于对使用 Cleaner
.
注册的实例的访问权限
简而言之,在您的代码中对 toBeCleanedWeakReference.get()
的调用将 return null
因为 ToBeCleaned
实例将被垃圾回收,至少从我们的角度来看是这样到那时。
正确的方法是以某种方式引用需要清理的资源,而无需“通过”ToBeCleaned
实例。通常这意味着:
使清理动作和资源成为同一个对象(区别于Cleaner
注册的对象)。 Cleaner
的 documentation 显示了这种方法的一个例子。
在实例化清理操作时将对资源的引用传递给清理操作,而不是向 Cleaner
注册的对象。这是一个例子:
public class ToBeCleaned implements AutoCloseable {
// documentation suggests you should preferably have one
// Cleaner instance per library
private static final Cleaner CLEANER = ...;
private final Cleaner.Cleanable cleanable;
private final SomeResource resource;
public ToBeCleaned() {
resource = ...;
cleanable = CLEANER.register(this, new CleaningAction(resource));
}
@Override
public void close() {
cleanable.clean();
}
private static class CleaningAction implements Runnable {
private final SomeResource resource;
CleaningAction(SomeResource resource) {
this.resource = resource;
}
@Override
public void run() {
// clean up 'resource'
}
}
}
两个示例都实现了 AutoCloseable
。这使 API 的用户能够释放资源 on-demand 而不是等待垃圾收集器启动(这使得 Cleaner
更像是一个“备份”)。
我需要清理 JNI 调用分配的资源。通过覆盖 Object.finalize()
方法很容易做到这一点。由于从 Java 9 开始不推荐使用此方法,因此我尝试使用新的 java.lang.ref.Cleaner
class.
下面是实例被垃圾回收前调用ToBeCleaned.cleanUp
方法的代码:
import java.lang.ref.Cleaner;
import java.lang.ref.WeakReference;
public class ToBeCleaned {
private static Cleaner cleaner = Cleaner.create();
public ToBeCleaned() {
cleaner.register(this, new CleanRunnable(this));
}
void cleanUp () {
// do cleanup
}
static class CleanRunnable implements Runnable {
// It has to be weak reference, otherwise ToBeCleaned instance
// would never be eligible for GC
private WeakReference<ToBeCleaned> toBeCleanedWeakReference;
CleanRunnable(ToBeCleaned toBeCleaned) {
this.toBeCleanedWeakReference = new WeakReference<>(toBeCleaned);
}
@Override
public void run() {
toBeCleanedWeakReference.get().cleanUp();
}
}
}
我的问题:这是正确的方法吗?
你的方法有缺陷。 “清理操作”不得依赖于对使用 Cleaner
.
简而言之,在您的代码中对 toBeCleanedWeakReference.get()
的调用将 return null
因为 ToBeCleaned
实例将被垃圾回收,至少从我们的角度来看是这样到那时。
正确的方法是以某种方式引用需要清理的资源,而无需“通过”ToBeCleaned
实例。通常这意味着:
使清理动作和资源成为同一个对象(区别于
Cleaner
注册的对象)。Cleaner
的 documentation 显示了这种方法的一个例子。在实例化清理操作时将对资源的引用传递给清理操作,而不是向
Cleaner
注册的对象。这是一个例子:public class ToBeCleaned implements AutoCloseable { // documentation suggests you should preferably have one // Cleaner instance per library private static final Cleaner CLEANER = ...; private final Cleaner.Cleanable cleanable; private final SomeResource resource; public ToBeCleaned() { resource = ...; cleanable = CLEANER.register(this, new CleaningAction(resource)); } @Override public void close() { cleanable.clean(); } private static class CleaningAction implements Runnable { private final SomeResource resource; CleaningAction(SomeResource resource) { this.resource = resource; } @Override public void run() { // clean up 'resource' } } }
两个示例都实现了 AutoCloseable
。这使 API 的用户能够释放资源 on-demand 而不是等待垃圾收集器启动(这使得 Cleaner
更像是一个“备份”)。