Surface 在 Android 上被打包后如何处理垃圾收集?

How does Surface handle garbage collection after being parcelled on Android?

我使用 source code for Surface.java 作为这个问题的参考。

Surface 实现了 Parcelable 接口,并且它还持有本机端对象的句柄。

我很想知道在这种情况下如何处理垃圾回收:

  1. 创建一个 Surface (A) 并将其写入 Parcel。之后再无引用。

  2. 从Parcel中读取原始Surface(B)的副本;假设这发生在另一个用于渲染的线程上。这个实例现在持有与 (A) 相同的本机句柄,并且在某处有对该实例的强引用。

  3. GC 发生并且 (A) 被收集,因为它不再被引用。 finalize() 是 运行,它调用 release(),后者又调用 nativeRelease(long) 作为本机句柄。

粗略查看了源代码,我认为现在 (B) 也应该结束并停止工作,因为本机句柄已释放,但在尝试复制后似乎并非如此. (A) 确实被收集了,但 (B) 继续存在并仍然可用。

现在我有一种感觉,本机对象正在进行一些引用计数,或者在 parcelling 过程的本机端有一些其他魔法。

不管我的假设是否正确,我正在寻找导致此行为的原因的概述,最好是对框架源代码的一些引用。我也对 Surface 锁定在类似情况下的工作方式很感兴趣。

表面只是对 BufferQueue. They contain a Binder token, used to negotiate sending graphical buffers between producer and receiver. A relevant JNI code:

的引用
static jlong nativeReadFromParcel(JNIEnv* env, jclass clazz, jlong nativeObject, jobject parcelObj) {
  Parcel* parcel = parcelForJavaObject(env, parcelObj);
  if (parcel == NULL) {
    doThrowNPE(env);
    return 0;
  }

  android::view::Surface surfaceShim;

  // Calling code in Surface.java has already read the name of the Surface
  // from the Parcel
  surfaceShim.readFromParcel(parcel, /*nameAlreadyRead*/true);

  sp<Surface> self(reinterpret_cast<Surface *>(nativeObject));

  // update the Surface only if the underlying IGraphicBufferProducer
  // has changed.
  if (self != nullptr
        && (IInterface::asBinder(self->getIGraphicBufferProducer()) ==
                IInterface::asBinder(surfaceShim.graphicBufferProducer))) {
      // same IGraphicBufferProducer, return ourselves
      return jlong(self.get());
  }

  sp<Surface> sur;
  if (surfaceShim.graphicBufferProducer != nullptr) {
    // we have a new IGraphicBufferProducer, create a new Surface for it
    sur = new Surface(surfaceShim.graphicBufferProducer, true);
    // and keep a reference before passing to java
    sur->incStrong(&sRefBaseOwner);
  }

  if (self != NULL) {
    // and loose the java reference to ourselves
    self->decStrong(&sRefBaseOwner);
  }

  return jlong(sur.get());
}

你可以清楚地看到,一个Binder令牌是如何从Parcel中读取并转换为IGraphicBufferProducer IPC接口的。

Binder 令牌在内核中被引用计数,销毁一个用户 space 引用不会做任何事情,只要存在更多。

当你在同一个进程中时,锁定语义不会改变,因为原生 Surface maintains a cache of instances:

sp<Surface> Surface::readFromParcel(const Parcel& data) {
  Mutex::Autolock _l(sCachedSurfacesLock);
  sp<IBinder> binder(data.readStrongBinder());
  sp<Surface> surface = sCachedSurfaces.valueFor(binder).promote();
  if (surface == 0) {
   surface = new Surface(data, binder);
   sCachedSurfaces.add(binder, surface);
  } else {
    // The Surface was found in the cache, but we still should clear any
    // remaining data from the parcel.
    data.readStrongBinder();  // ISurfaceTexture
    data.readInt32();         // identity
  }
  if (surface->mSurface == NULL && surface->getISurfaceTexture() == NULL) {
    surface = 0;
  }
  cleanCachedSurfacesLocked();
  return surface;
}

每个 Java Surface 实例,由 parcelling/unparcelling 在同一个进程中创建,引用同一个本机 Surface,这意味着锁应该仍然有效:如果发生争用,你会得到一个例外。

尝试从多个进程同时绘制到未分割的表面将失败,因为 IGraphicBufferProducer 合同 explicitly forbids that:

// connect attempts to connect a client API to the IGraphicBufferProducer.
// This must be called before any other IGraphicBufferProducer methods are
// called except for getAllocator.
//
// This method will fail if the connect was previously called on the
// IGraphicBufferProducer and no corresponding disconnect call was made.
//
// outWidth, outHeight and outTransform are filled with the default width
// and height of the window and current transform applied to buffers,
// respectively. The token needs to be any binder object that lives in the
// producer process -- it is solely used for obtaining a death notification
// when the producer is killed.
virtual status_t connect(const sp<IBinder>& token,
        int api, bool producerControlledByApp, QueueBufferOutput* output) = 0;

您可以在 Android website for device and firmware makers.

上找到有关较低级别图形堆栈体系结构的更多详细信息