英文:
Usage of java.lang.ref.Cleaner as alternative to Object.finalize
问题
以下是翻译好的内容:
我需要清理通过JNI调用分配的资源。通过重写 Object.finalize()
方法很容易实现。由于从Java 9开始该方法已被弃用,因此我尝试使用新的 java.lang.ref.Cleaner
类来实现相同的功能。
下面是在实例被垃圾回收之前调用 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() {
// 进行清理操作
}
static class CleanRunnable implements Runnable {
// 必须使用弱引用,否则 ToBeCleaned 实例将永远不会被标记为可回收
private WeakReference<ToBeCleaned> toBeCleanedWeakReference;
CleanRunnable(ToBeCleaned toBeCleaned) {
this.toBeCleanedWeakReference = new WeakReference<>(toBeCleaned);
}
@Override
public void run() {
toBeCleanedWeakReference.get().cleanUp();
}
}
}
我的问题:这是否是正确的方法?
英文:
I need to clean up resources allocated by a JNI call. It's easy to do it by overriding Object.finalize()
method. Since this method is deprecated starting with Java 9, I'm trying to achieve the same thing using the new java.lang.ref.Cleaner
class.
Here is the code for calling ToBeCleaned.cleanUp
method before the instance is garbage collected:
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();
}
}
}
My question: Is this the right approach?
答案1
得分: 5
你的方法存在一个缺陷。"清理操作"不能依赖于对注册在Cleaner
中的实例的访问。
简而言之,你代码中的toBeCleanedWeakReference.get()
调用会返回null
,因为从我们的角度来看,ToBeCleaned
实例在那时可能已经被垃圾回收。
正确的方法是在不"通过" ToBeCleaned
实例的情况下引用需要清理的资源。通常有以下两种方式:
-
使清理操作和资源成为同一个对象(与注册在
Cleaner
中的对象不同)。Cleaner
的文档展示了这种方法的示例。 -
在实例化清理操作时,向其传递对资源的引用,而不是注册在
Cleaner
中的对象。以下是一个示例:public class ToBeCleaned implements AutoCloseable { // 文档建议您最好为每个库使用一个 Cleaner 实例 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() { // 清理 'resource' } } }
这两个示例都实现了 AutoCloseable
接口。这使得您的 API 用户能够根据需要释放资源,而不必等待垃圾收集器启动(这使得Cleaner
更像是一个"备份")。
英文:
Your approach has a flaw. The "cleaning action" must not depend on having access to the instance registered with the Cleaner
.
In short, the call to toBeCleanedWeakReference.get()
in your code will return null
since the ToBeCleaned
instance will have been, at least from our perspective, garbage collected by that point.
The correct approach is to somehow reference the resource that needs to be cleaned up without "going through" the ToBeCleaned
instance. Typically this means either:
-
Making the cleaning action and the resource the same object (distinct from the object registered with the
Cleaner
). The documentation ofCleaner
shows an example of this approach. -
Passing a reference to the resource, but not the object registered with the
Cleaner
, to the cleaning action when instantiating it. Here's an example: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' } } }
Both examples implement AutoCloseable
. That gives users of your API the ability to release the resources on-demand rather than waiting for the garbage collector to kick in (which makes the Cleaner
more of a "back up").
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论