使用 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 实例。通常这意味着:

  1. 使清理动作和资源成为同一个对象(区别于Cleaner注册的对象)。 Cleanerdocumentation 显示了这种方法的一个例子。

  2. 在实例化清理操作时将对资源的引用传递给清理操作,而不是向 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 更像是一个“备份”)。