删除具有通用 PhantomReference class 的本机对等体

Delete native peer with general PhantomReference class

正如 Hans Boehm 在 Google I/O '17 talk "How to Manage Native C++ Memory in Android" 中建议我使用 PhantomReferenceclass 来确保本地对等点被删除正确。

18 min 57 sec he shows an example implementation of an object registering itself to the PhantomReference class for it's type. This PhantomReference class, he shows then at 19 min 49 sec 的链接视频中。所以我为我的示例对象复制了他的方法。见下文。

虽然这种方法工作正常,但无法扩展。我需要创建相当多的对象,但我还没有找到创建基础的方法class(对于我的对象或 PhantomReference 基础 class),它将接受任何对象并正确处理本机删除。

如何创建一个通用基础 PhantomReference class 来调用所提供对象的本地静态方法?

我试图转换 PhantomReference 泛型,但本机静态删除方法阻碍了实现。

我的WorkViewModel

import android.databinding.*;

public class WorkViewModel extends BaseObservable
{
  private long _nativeHandle;

  public WorkViewModel(Database database, int workId)
  {
    _nativeHandle = create(database.getNativeHandle(), workId);
    WorkViewModelPhantomReference.register(this, _nativeHandle);
  }

  private static native long create(long databaseHandle, int workId);
  static native void delete(long nativeHandle);

  @Bindable
  public native int getWorkId();
  public native void setWorkId(int workId);
}

我的WorkViewModelPhantomReference

import java.lang.ref.*;
import java.util.*;

public class WorkViewModelPhantomReference extends PhantomReference<WorkViewModel>
{
  private static Set<WorkViewModelPhantomReference> phantomReferences = new HashSet<WorkViewModelPhantomReference>();
  private static ReferenceQueue<WorkViewModel> garbageCollectedObjectsQueue = new ReferenceQueue<WorkViewModel>();
  private long _nativeHandle;

  private WorkViewModelPhantomReference(WorkViewModel workViewModel, long nativeHandle)
  {
    super(workViewModel, garbageCollectedObjectsQueue);
    _nativeHandle = nativeHandle;
  }

  public static void register(WorkViewModel workViewModel, long nativeHandle)
  {
    phantomReferences.add(new WorkViewModelPhantomReference(workViewModel, nativeHandle));
  }

  public static void deleteOrphanedNativePeerObjects()
  {
    WorkViewModelPhantomReference reference;

    while((reference = (WorkViewModelPhantomReference)garbageCollectedObjectsQueue.poll()) != null)
    {
      WorkViewModel.delete(reference._nativeHandle);
      phantomReferences.remove(reference);
    }
  }
}

您可以查看 Java 9 的 Cleaner API,它解决了类似的任务,围绕 PhantomReference 构建的清理并实现了类似的事情,根据您的需要进行调整。由于您不需要支持多个清洁器,因此您可以使用 static 注册方法。我建议保留引用的抽象,即 Cleanable 接口,以确保不能调用继承的引用方法,尤其是 clear()clean() 很容易混淆:

public class Cleaner {
    public interface Cleanable {
        void clean();
    }
    public static Cleanable register(Object o, Runnable r) {
        CleanerReference c = new CleanerReference(
                Objects.requireNonNull(o), Objects.requireNonNull(r));
        phantomReferences.add(c);
        return c;
    }
    private static final Set<CleanerReference> phantomReferences
                                             = ConcurrentHashMap.newKeySet();
    private static final ReferenceQueue<Object> garbageCollectedObjectsQueue
                                              = new ReferenceQueue<>();

    static final class CleanerReference extends PhantomReference<Object>
                                        implements Cleanable {
        private final Runnable cleaningAction;

        CleanerReference(Object referent, Runnable action) {
            super(referent, garbageCollectedObjectsQueue);
            cleaningAction = action;
        }
        public void clean() {
            if(phantomReferences.remove(this)) {
                super.clear();
                cleaningAction.run();
            }
        }
    }
    public static void deleteOrphanedNativePeerObjects() {
        CleanerReference reference;
        while((reference=(CleanerReference)garbageCollectedObjectsQueue.poll()) != null) {
            reference.clean();
        }
    }
}

这使用了 Java 8 个特征;如果 ConcurrentHashMap.newKeySet() 不可用,您可以使用 Collections.newSetFromMap(new ConcurrentHashMap<CleanerReference,Boolean>()) 代替。

它保留了 deleteOrphanedNativePeerObjects() 以显式触发清理,但它是线程安全的,因此创建一个守护进程后台线程来在项目入队后立即清理项目是没有问题的,就像原来的那样.

将操作表达为 Runnable 允许将其用于任意资源,而返回 Cleanable 允许支持显式清理而不依赖垃圾收集器,同时仍然为这些资源提供安全网尚未关闭的对象。

public class WorkViewModel extends BaseObservable implements AutoCloseable
{
    private long _nativeHandle;
    Cleaner.Cleanable cleanable;

    public WorkViewModel(Database database, int workId)
    {
      _nativeHandle = create(database.getNativeHandle(), workId);
      cleanable = createCleanable(this, _nativeHandle);
    }
    private static Cleaner.Cleanable createCleanable(Object o, long _nativeHandle) {
        return Cleaner.register(o, () -> delete(_nativeHandle));
    }

    @Override
    public void close() {
        cleanable.clean();
    }

    private static native long create(long databaseHandle, int workId);
    static native void delete(long nativeHandle);

    @Bindable
    public native int getWorkId();
    public native void setWorkId(int workId);

}

通过实现 AutoCloseable,它可以与 try-with-resources 结构一起使用,但如果没有简单的块作用域,也可以手动调用 close()。手动关闭它的好处,不仅是底层资源更早关闭,而且幻象对象从 Set 中移除,永远不会入队,使整个生命周期更有效率,尤其是当你创建并在短期内使用很多对象。但是如果 close() 没有被调用,清洁器最终会被垃圾收集器排入队列。

对于支持手动关闭的 classes,保留一个标志将很有用,以检测并拒绝在关闭后使用它的尝试。

如果 lambda 表达式不适用于您的目标,您可以通过 inner class 实现 Runnable;它仍然比创建另一个虚引用的子 class 更简单。必须注意不要捕获 this 实例,这就是为什么在上面的示例中将创建移动到 static 方法中的原因。如果范围内没有 this,就不会被意外捕获。该方法还将引用对象声明为 Object,以强制使用参数值而不是实例字段。