Surface 在 Android 上被打包后如何处理垃圾收集?
How does Surface handle garbage collection after being parcelled on Android?
我使用 source code for Surface.java
作为这个问题的参考。
Surface 实现了 Parcelable 接口,并且它还持有本机端对象的句柄。
我很想知道在这种情况下如何处理垃圾回收:
创建一个 Surface (A) 并将其写入 Parcel。之后再无引用。
从Parcel中读取原始Surface(B)的副本;假设这发生在另一个用于渲染的线程上。这个实例现在持有与 (A) 相同的本机句柄,并且在某处有对该实例的强引用。
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;
上找到有关较低级别图形堆栈体系结构的更多详细信息
我使用 source code for Surface.java
作为这个问题的参考。
Surface 实现了 Parcelable 接口,并且它还持有本机端对象的句柄。
我很想知道在这种情况下如何处理垃圾回收:
创建一个 Surface (A) 并将其写入 Parcel。之后再无引用。
从Parcel中读取原始Surface(B)的副本;假设这发生在另一个用于渲染的线程上。这个实例现在持有与 (A) 相同的本机句柄,并且在某处有对该实例的强引用。
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;
上找到有关较低级别图形堆栈体系结构的更多详细信息