2019-02-01
我们知道在 Java 中是没有析构函数这个机制的。在学习 React Native 代码的时候,我发现他实现了一种机制,给 Java 对象添加了析构函数的机制。
在本文中我们一探究竟。
我们从 DestructorThread 入手,它内部包含一条线程,他会在对象被垃圾回收时执行一个析构的机制。这是给 Java 对象添加了一个析构方法。
从中我们可以理解:我们启动一条线程,它监听 JVM 的垃圾回收过程,hook 住对象被回收的时机,并在对象被回收前触发回调,这个回调就是对象的析构函数。
Destructor 是对象的析构器,它内部含有析构函数。需要析构特性的对象,需要创建一个 DestructorThread.Destructor 的静态子类,一旦引用对象被垃圾回收,DestructorThread 就会调用到 Destructor#destruct() 方法。
它的实现如下:
public abstract static class Destructor extends PhantomReference<Object> { private Destructor next; private Destructor previous; Destructor(Object referent) { super(referent, sReferenceQueue); sDestructorStack.push(this); } private Destructor() { super(null, sReferenceQueue); } /** Callback which is invoked when the original object has been garbage collected. */ abstract void destruct(); }
前面我们说到,监听 JVM 的垃圾回收过程,hook 住对象被回收的时机,这是怎么实现的呢?答案就在这里,我们使用了 PhantomReference(虚引用)。虚引用是 Java 对象引用方式的一种,由于平时较少用到所以很多人可能不太了解,它的作用就是给一个对象释放的 hook 时机。需要注意的是,它必须要同 ReferenceQueue 一起使用。
其中:
除此之外,我们还注意到 Destructor 自身是一个链表结构,它持有前后对象的引用。
我们来看 DestructorThread 的静态区:
static { sDestructorStack = new DestructorStack(); sReferenceQueue = new ReferenceQueue(); sDestructorList = new DestructorList(); sThread = new Thread("HybridData DestructorThread") { @Override public void run() { while (true) { try { Destructor current = (Destructor) sReferenceQueue.remove(); current.destruct(); if (current.previous == null) { sDestructorStack.transferAllToList(); } DestructorList.drop(current); } catch (InterruptedException e) {} } } }; sThread.start(); }
这里初始化了几个成员,他们的类型分别为:
private static DestructorList sDestructorList; private static DestructorStack sDestructorStack; private static ReferenceQueue sReferenceQueue; private static Thread sThread;
下面我们来分析这个过程。
ReferenceQueue 是一种特殊的队列,称为引用队列。
它的使用方法是这样的:
ReferenceQueue sReferenceQueue = new ReferenceQueue() // ... while(true) { try { Object o = sReferenceQueue.remove(); } catch (InterruptedException e) {} }
其中:
我们来看 DestructorStack 的实现:
/** This is a thread safe, lock-free Treiber-like Stack of Destructors. */ private static class DestructorStack { private AtomicReference<Destructor> mHead = new AtomicReference<>(); public void push(Destructor newHead) { Destructor oldHead; do { oldHead = mHead.get(); newHead.next = oldHead; } while (!mHead.compareAndSet(oldHead, newHead)); } public void transferAllToList() { Destructor current = mHead.getAndSet(null); while (current != null) { Destructor next = current.next; sDestructorList.enqueue(current); current = next; } } }
其中:
DestructorList 的作用是持有所有内存中活跃的析构器(析构函数是以类实例的形式存在的)。
它是一个静态类,我们看下它的实现:
/** A doubly-linked list of Destructors. */ private static class DestructorList { private Destructor mHead; public DestructorList() { mHead = new Terminus(); mHead.next = new Terminus(); mHead.next.previous = mHead; } public void enqueue(Destructor current) { current.next = mHead.next; mHead.next = current; current.next.previous = current; current.previous = mHead; } private static void drop(Destructor current) { current.next.previous = current.previous; current.previous.next = current.next; } }
其中,Terminus 是析构器的一个默认实现:
private static class Terminus extends Destructor { @Override void destruct() { throw new IllegalStateException("Cannot destroy Terminus Destructor."); } }
这个类执行了入队和出队操作。
前面我们将各个部分分别进行了学习,在本节中结合起来分析。
在 Destructor 构造的时候,DestructorThread 的静态区会被执行,DestructorList、DestructorStack、ReferenceQueue 和线程都被初始化。
在 Destructor 的构造函数中:
private Destructor() { super(null, sReferenceQueue); sDestructorStack.push(this); }
会建立虚引用,并绑定到 sReferenceQueue 中。
同时会向栈中入栈,我们来看第一次入栈时的 DestructorStack.push 的情景:
这样如果对象被释放,我们起的线程会有反应:
sThread = new Thread("HybridData DestructorThread") { @Override public void run() { while (true) { try { Destructor current = (Destructor) sReferenceQueue.remove(); current.destruct(); if (current.previous == null) { sDestructorStack.transferAllToList(); } DestructorList.drop(current); } catch (InterruptedException e) { // Continue. This thread should never be terminated. } } } };
其中:
在上一节中我们分析了 DestructorThread 内部的工作原理。在本节中我们来看是如何使用这个析构机制的。
在 React Native 中,使用到这个机制的类是 HybridData 类。我们看下它的实现:
public class HybridData { static { SoLoader.loadLibrary("fb"); } private Destructor mDestructor = new Destructor(this); public synchronized void resetNative() { mDestructor.destruct(); } public boolean isValid() { return mDestructor.mNativePointer != 0; } public static class Destructor extends DestructorThread.Destructor { private long mNativePointer; Destructor(Object referent) { super(referent); } @Override void destruct() { deleteNative(mNativePointer); mNativePointer = 0; } static native void deleteNative(long pointer); } }
其中:
HybridData 这个类在 ReactNative 中广泛被使用,它是一种特殊的对象,虽然它是一个 Java 对象,但它同时引用这一个 Native 对象,并且在 Java 对象回收的时候,Native 也同时回收。
介绍
下面我们来看,在 CatalystInstanceImpl 中是如何使用 HybridData 的。
在 CatalystInstanceImpl 中有两个成员:
private final HybridData mHybridData; private native static HybridData initHybrid();
在构造函数中:
mHybridData = initHybrid();
我们可以看出,在构函数中,CatalystInstanceImpl 调用了原生的 initHybrid 方法,并将返回值存入 mHybridData。
下面我们进入 Native 层,看看 initHybrid 的实现。
Native 层 initHybrid
在 ReactAndroid/src/main/jni/react/jni/CatalystInstanceImpl.cpp 的 CatalystInstanceImpl::initHybrid,对应实现为:
jni::local_ref<CatalystInstanceImpl::jhybriddata> CatalystInstanceImpl::initHybrid( jni::alias_ref<jclass>) { return makeCxxInstance(); }
其中 makeCxxInstance 定义在 ReactAndroid/src/main/jni/first-party/fb/include/fb/fbjni/Hybrid.h 中:
template<typename T> local_ref<HybridDestructor> getHolder(T t) { static auto holderField = t->getClass()->template getField<HybridDestructor::javaobject>("mDestructor"); return t->getFieldValue(holderField); } template<typename T> void setNativePointer(T t, std::unique_ptr<detail::BaseHybridClass> new_value) { getHolder(t)->setNativePointer(std::move(new_value)); } static local_ref<detail::HybridData> makeHybridData(std::unique_ptr<T> cxxPart) { auto hybridData = detail::HybridData::create(); setNativePointer(hybridData, std::move(cxxPart)); return hybridData; } template <typename... Args> static local_ref<detail::HybridData> makeCxxInstance(Args&&... args) { return makeHybridData(std::unique_ptr<T>(new T(std::forward<Args>(args)...))); }
这段 C++ 代码看起来想当费劲,大体可以知道:
在本文中,我们分析了 React Native 中,给 Java 添加析构机制,其实质是使用了 Java 的虚引用特性。
同时我们也分析了 React Native 中使用这一机制的 HybridData 类,并用 CatalystInstanceImpl 为例,分析了它的实际用法。