正如Google I/O '17上的Hans Boehm所说,“How to Manage Native C++ Memory in Android”建议我使用PhantomReference类来确保正确删除 native 对等项。

18 min 57 sec的链接视频中,他展示了一个对象的示例实现,该对象将其自身注册到PhantomReference类中。然后,他在19 min 49 sec上显示这个PhantomReference类。因此,我将他的方法复制为示例对象。见下文。

尽管此方法可以正常工作,但无法扩展。 我将需要创建相当数量的对象,但我还没有找到一种创建基类的方法(无论是针对我的对象还是PhantomReference基类),该方法可以接收任何对象并可以正确处理 native 删除。

如何创建一个通用的PhantomReference基类,它可以在提供的对象上调用 native 静态方法?

我尝试过转换PhantomReference泛型,但是 native 静态删除方法妨碍了实现。

我的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,该API解决了类似的任务,围绕PhantomReference进行清理,并实现了类似的东西,使其适应您的需求。由于您不需要支持多个清理器,因此可以使用static注册方法。我建议保留引用的抽象,即Cleanable接口(interface),以确保不能调用任何继承的引用方法,特别是因为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()来显式触发清理,但是它是线程安全的,因此创建一个守护程序后台线程来将项目在排队后立即进行清理是没有问题的,就像原始项目一样。

将 Action 表示为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(),则清理器最终会被垃圾收集器排入队列。

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

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

关于java - 使用常规PhantomReference类删除 native 对等体,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46144524/

10-09 03:30