Bru*_*eri 6 java android memory-management phantom-reference
正如Google I/O '17中的Hans Boehm所说的" 如何在Android中管理本机C++内存 "建议我使用PhantomReference该类来确保正确删除本地对等体.
在18分57秒的链接视频中,他展示了一个对象的示例实现,该对象将其自身注册到PhantomReference类的类型.这PhantomReference节课,他以19分49秒的成绩表演.所以我为我的示例对象复制了他的方法.见下文.
虽然这种方法很好,但它不能扩展.我将需要创建相当多的对象,我还没有找到一种方法来创建一个基类(对于我的对象或PhantomReference基类),它将接受任何对象,并将正确处理本机删除.
如何创建一个PhantomReference可以在提供的对象上调用本机静态方法的通用基类?
我试图转换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);
}
Run Code Online (Sandbox Code Playgroud)
我的 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);
}
}
}
Run Code Online (Sandbox Code Playgroud)
您可以查看Java 9的CleanerAPI,它可以解决类似的任务,围绕a进行清理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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
这使用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);
}
Run Code Online (Sandbox Code Playgroud)
通过实现AutoCloseable,它可以与try-with-resources构造一起使用,但close()如果没有简单的块作用域,也可以手动调用.手动关闭它的好处不仅在于底层资源更早关闭,幻像对象也从中移除Set并且永远不会排队,从而使整个生命周期更加高效,尤其是当您创建和使用大量对象时简而言之.但是如果close()没有被调用,清洁工最终会被垃圾收集器排队.
对于支持手动关闭的类,保留标志将非常有用,可以在关闭后检测并拒绝尝试使用它.
如果lambda表达式不适用于您的目标,则可以Runnable通过内部类实现; 它仍然比创建幻像引用的另一个子类更简单.必须注意不要捕获this实例,这就是为什么创建已被移动到static上面示例中的方法中的原因.如果没有this范围,则无法意外捕获.该方法还将引用声明Object为强制使用参数值而不是实例字段.