最近碰到一个问题:压测SurfaceFlinger 会crash掉,提示
SurfaceFlinger: Suspected IGBP leak: 4097 IGBPs (4096 max), 49 Layers
本文侧重点是介绍IGBP的创建与销毁。
IGraphicBufferProducer 是一个binder 接口,下图是这个IGraphicBufferProducer binder的结构关系;具体该图关于Binder的部分后续再写文章介绍。
一. IGraphicBufferProducer 的server端
该log是SurfaceFlinger.cpp中打印的,其代码段如下
status_t SurfaceFlinger::addClientLayer(const sp<Client>& client, const sp<IBinder>& handle,
const sp<IGraphicBufferProducer>& gbc, const sp<Layer>& lbc,
const sp<IBinder>& parentHandle,
const sp<Layer>& parentLayer, bool addToCurrentState,
uint32_t* outTransformHint) {
// add this layer to the current state list
{
Mutex::Autolock _l(mStateLock);
sp<Layer> parent;
if (parentHandle != nullptr) {
parent = fromHandleLocked(parentHandle).promote();
if (parent == nullptr) {
return NAME_NOT_FOUND;
}
} else {
parent = parentLayer;
}
if (mNumLayers >= ISurfaceComposer::MAX_LAYERS) {
ALOGE("AddClientLayer failed, mNumLayers (%zu) >= MAX_LAYERS (%zu)", mNumLayers.load(),
ISurfaceComposer::MAX_LAYERS);
return NO_MEMORY;
}
mLayersByLocalBinderToken.emplace(handle->localBinder(), lbc);
if (parent == nullptr && addToCurrentState) {
mCurrentState.layersSortedByZ.add(lbc);
} else if (parent == nullptr) {
lbc->onRemovedFromCurrentState();
} else if (parent->isRemovedFromCurrentState()) {
parent->addChild(lbc);
lbc->onRemovedFromCurrentState();
} else {
parent->addChild(lbc);
}
if (gbc != nullptr) {
mGraphicBufferProducerList.insert(IInterface::asBinder(gbc).get());
LOG_ALWAYS_FATAL_IF(mGraphicBufferProducerList.size() >
mMaxGraphicBufferProducerListSize,
"Suspected IGBP leak: %zu IGBPs (%zu max), %zu Layers",
mGraphicBufferProducerList.size(),
mMaxGraphicBufferProducerListSize, mNumLayers.load());
}
if (const auto display = getDefaultDisplayDeviceLocked()) {
lbc->updateTransformHint(display->getTransformHint());
}
if (outTransformHint) {
*outTransformHint = lbc->getTransformHint();
}
mLayersAdded = true;
}
// attach this layer to the client
client->attachLayer(handle, lbc);
return NO_ERROR;
}
可以看出来是addClientLayer调用的。
addClientLayer是由SurfaceFlinger的createLayer调用到的,如下图创建的几种layer形式。
check上面的crash问题点的log,有判断gbc != nullptr, 看上面的四个layer只有BufferQueueLayer对应的createBufferQueueLayer中设置了gbp,该值为layer->getProducer();
通过以下代码看出该Producer为MonitoredProducer,是一个IGraphicBufferProducer的server端Bn实现类
sp<IGraphicBufferProducer> mProducer;
void BufferQueueLayer::onFirstRef() {
mProducer = mFlinger->getFactory().createMonitoredProducer(producer, mFlinger, this);
}
sp<IGraphicBufferProducer> DefaultFactory::createMonitoredProducer(
const sp<IGraphicBufferProducer>& producer, const sp<SurfaceFlinger>& flinger,
const wp<Layer>& layer) {
return new MonitoredProducer(producer, flinger, layer);
}
// MonitoredProducer wraps an IGraphicBufferProducer so that SurfaceFlinger will
// be notified upon its destruction
class MonitoredProducer : public BnGraphicBufferProducer {}
二. IGraphicBufferProducer 的client端
我们看这个gbp最早出现的地方是在SurfaceComposerClient的createSurfaceChecked中,定义了个空的sp<IGraphicBufferProducer> gbp; 通过mClient->createSurface 获取到真正的SurfaceFlinger中的gbp。
此处看到将从SurfaceFlinger获取到的gbp和handle存放到了SurfaceControl中,并赋值给outSurface。 后面介绍这个outSurface。
status_t SurfaceComposerClient::createSurfaceChecked(const String8& name, uint32_t w, uint32_t h,
PixelFormat format,
sp<SurfaceControl>* outSurface, uint32_t flags,
SurfaceControl* parent, LayerMetadata metadata,
uint32_t* outTransformHint) {
sp<SurfaceControl> sur;
status_t err = mStatus;
if (mStatus == NO_ERROR) {
sp<IBinder> handle;
sp<IBinder> parentHandle;
sp<IGraphicBufferProducer> gbp;
if (parent != nullptr) {
parentHandle = parent->getHandle();
}
uint32_t transformHint = 0;
err = mClient->createSurface(name, w, h, format, flags, parentHandle, std::move(metadata),
&handle, &gbp, &transformHint);
if (outTransformHint) {
*outTransformHint = transformHint;
}
ALOGE_IF(err, "SurfaceComposerClient::createSurface error %s", strerror(-err));
if (err == NO_ERROR) {
*outSurface = new SurfaceControl(this, handle, gbp, transformHint);
}
}
return err;
}
这个createSurface的调用关系图:
所以调用createSurface时都是调用的上面的SurfaceFlinger的createLayer。
但layer区分状态,这个后面部分再说。
先介绍下上面几个类的关系
SurfaceComposerClient是binder ISurfaceComposerClient的wrapper 封装类。一般是SurfaceView和WMS中使用。
ISurfaceComposerClient是一个binder 对象,client端属于SurfaceComposerClient一次,server在SurfaceFlinger中,就是Client。
client属于SurfaceFlinger,实现BnSurfaceComposerClient。在SurfaceFlinger的createConnection中new 该对象,同时传进去SurfaceFlinger,供其调用。
SurfaceFlinger就是显示的核心模块,其中实现createLayer在第一部分已介绍,创建BufferQueueLayer,并创建GraphicBufferProducer通过该接口返回该值。
上面几个类是如何建立联系呢
A new SurfaceComposerClient后,拿到第一个强引用sp时,会调用该类的onFirstRet方法,该方法会先与SurfaceFlinger进程建立通信连接。
B SurfaceComposerClient与SurfaceFlinger的连接关系是使用ComposerService去维护的,ComposerService::getComposerService() 内部实现使用单例模式,保证进程中只建立一个有效连接,防止连接不在时重新再建立连接,所以不要保存该连接的副本;同时该API中会去等待直到拿到SurfaceFlinger : BnSurfaceComposer的binder返回值。
C 拿到连接引用后,就可以调用SurfaceFlinger的实现了:createConnection,进而创建client:BnSurfaceComposerClient。
D 这样就可以call createSurface 创建Layer和IGBP
下图画出了部分流程图:
这边忽略了Binder对象(IGBP、ISurfaceComposer等)在API中的具体传递,涉及到Binder的通信部分,后续介绍binder时再细说。
三. IGraphicBufferProducer的client 端使用
grep code发现SurfaceSession的jni中new了SurfaceComposerClient,并将new的指针reinterpret_cast成long型存到了java object中mNativeClient。
这个SurfaceSession是给SurfaceControl使用的,SurfaceControl的jni中nativeCreate就是使用SurfaceSession的SurfaceComposerClient去调用createSurfaceChecked创建Surface的。
看下面这段SurfaceControl的jni中nativeCreate code,通过android_view_SurfaceSession_getClient获取SurfaceSession创建的SurfaceComposerClient 引用。将其变量mNativeClient reinterpret_cast成SurfaceComposerClient。
然后调用SurfaceComposerClient 的createSurfaceChecked方法。
sp<SurfaceComposerClient> android_view_SurfaceSession_getClient(
JNIEnv* env, jobject surfaceSessionObj) {
return reinterpret_cast<SurfaceComposerClient*>(
env->GetLongField(surfaceSessionObj, gSurfaceSessionClassInfo.mNativeClient));
}
static jlong nativeCreate(JNIEnv* env, jclass clazz, jobject sessionObj,
jstring nameStr, jint w, jint h, jint format, jint flags, jlong parentObject,
jobject metadataParcel) {
ScopedUtfChars name(env, nameStr);
sp<SurfaceComposerClient> client;
if (sessionObj != NULL) {
client = android_view_SurfaceSession_getClient(env, sessionObj);
} else {
client = SurfaceComposerClient::getDefault();
}
SurfaceControl *parent = reinterpret_cast<SurfaceControl*>(parentObject);
sp<SurfaceControl> surface;
LayerMetadata metadata;
Parcel* parcel = parcelForJavaObject(env, metadataParcel);
if (parcel && !parcel->objectsCount()) {
status_t err = metadata.readFromParcel(parcel);
if (err != NO_ERROR) {
jniThrowException(env, "java/lang/IllegalArgumentException",
"Metadata parcel has wrong format");
}
}
status_t err = client->createSurfaceChecked(
String8(name.c_str()), w, h, format, &surface, flags, parent, std::move(metadata));
if (err == NAME_NOT_FOUND) {
jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
return 0;
} else if (err != NO_ERROR) {
jniThrowException(env, OutOfResourcesException, NULL);
return 0;
}
surface->incStrong((void *)nativeCreate);
return reinterpret_cast<jlong>(surface.get());
}
上文中提到将从SurfaceFlinger中获取的GBP和handle存放到了outSurface中,此处将outSurface进行sp incStrong并reinterpret_cast成long型返回给SurfaceControl java object中存起来,这个值在java层是mNativeObject。
这边就有两个cpp层sp 指针reinterpret_cast 成long存到java side:一个是SurfaceSession中的mNativeClient,对应cpp的是SurfaceComposerClient; 另一个是SurfaceControl的mNativeObject,对应cpp的是SurfaceControl。
这边java side和cpp side的对应关系,有强引用计数,所以是绑定的关系,需要后面去释放,否则java object在GC自动释放后,cpp层反而一直不能析构掉,而出现内存泄漏memory leak问题。后面部分再介绍释放的问题。
SurfaceControl的java作用有很多了,大部分是有个transaction设置窗口的属性如setlayer、size。此处不在本文介绍中。
我们看SurfaceControl的cpp code,内容不多,大部分与Surface有关系。
我们就开始看Surface的内容,cpp层Surface的创建使用的是IGraphicBufferProducer,这个IGBP就是SurfaceControl中的,那就是Surface是从SurfaceControl中创建的。如下code所示
sp<Surface> SurfaceControl::generateSurfaceLocked() const
{
// This surface is always consumed by SurfaceFlinger, so the
// producerControlledByApp value doesn't matter; using false.
mSurfaceData = new Surface(mGraphicBufferProducer, false);
return mSurfaceData;
}
sp<Surface> SurfaceControl::getSurface() const
{
Mutex::Autolock _l(mLock);
if (mSurfaceData == nullptr) {
return generateSurfaceLocked();
}
return mSurfaceData;
}
sp<Surface> SurfaceControl::createSurface() const
{
Mutex::Autolock _l(mLock);
return generateSurfaceLocked();
}
所以IGBP就是由两个cpp class进行了强引用计数:SurfaceControl和Surface;
如果IGBP要释放的话,就需要两个class都析构掉。
Surface java 层的创建是在哪边开始的呢?
它是在SurfaceView 和ViewRootImpl中,java层的Surface最开始与cpp Surface没有关系的,只是一个空的容器,最开始的创建是随着SurfaceView和ViewRootImpl创建就有了。真正与cpp surface绑定是在有了SurfaceControl后,调用Surface.copyFrom(SurfaceControl other).
所以这样看,java Surface一个生命周期中可以和很多个cpp层的Surface绑定。
如果有很多的IGBP 没有释放,与cpp surface 有关,与java的Surface依赖度较低。java 的object析构后也只能释放最后拿着的cpp Surface。
public void copyFrom(SurfaceControl other) {
if (other == null) {
throw new IllegalArgumentException("other must not be null");
}
long surfaceControlPtr = other.mNativeObject;
if (surfaceControlPtr == 0) {
throw new NullPointerException(
"null SurfaceControl native object. Are you using a released SurfaceControl?");
}
long newNativeObject = nativeGetFromSurfaceControl(mNativeObject, surfaceControlPtr);
synchronized (mLock) {
if (newNativeObject == mNativeObject) {
return;
}
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
}
setNativeObjectLocked(newNativeObject);
}
}
static jlong nativeGetFromSurfaceControl(JNIEnv* env, jclass clazz,
jlong nativeObject,
jlong surfaceControlNativeObj) {
Surface* self(reinterpret_cast<Surface *>(nativeObject));
sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(surfaceControlNativeObj));
// If the underlying IGBP's are the same, we don't need to do anything.
if (self != nullptr &&
IInterface::asBinder(self->getIGraphicBufferProducer()) ==
IInterface::asBinder(ctrl->getIGraphicBufferProducer())) {
return nativeObject;
}
sp<Surface> surface(ctrl->getSurface());
if (surface != NULL) {
surface->incStrong(&sRefBaseOwner);
}
return reinterpret_cast<jlong>(surface.get());
}
这边又看到一个cpp层sp 指针reinterpret_cast 成long存到java side-- mNativeObject。
四 SurfaceControl 对应的layer 类型
如第一部分介绍,layer有BufferQueueLayer/BufferStateLayer/EffectLayer/ContainerLayer. 由上面介绍可知,每创建一个layer都会同时创建一个SurfaceControl存放对应layer的handle或者IGBP。
这四种layer分别使用不同的flag进行区分:SurfaceFlinger中flag为:ISurfaceComposerClient::
eFXSurfaceBufferQueue
/eFXSurfaceBufferState
/eFXSurfaceEffect
/eFXSurfaceContainer。
check source code发现SurfaceControl java side也有对应的四种flag:
FX_SURFACE_NORMAL
/FX_SURFACE_BLAST
/FX_SURFACE_EFFECT
/FX_SURFACE_CONTAINER.
FX_SURFACE_NORMAL flag对应的layer才是BufferQueueLayer,是在下面的code中设置的。
@NonNull
public Builder setBufferSize(@IntRange(from = 0) int width,
@IntRange(from = 0) int height) {
if (width < 0 || height < 0) {
throw new IllegalArgumentException(
"width and height must be positive");
}
mWidth = width;
mHeight = height;
// set this as a buffer layer since we are specifying a buffer size.
return setFlags(FX_SURFACE_NORMAL, FX_SURFACE_MASK);
}
所以setBufferSize 调用地方常见的就有了SurfaceView和WindowSurfaceController。WindowSurfaceController是给WMS使用。WindowSurfaceController是WMS给上面提到的ViewRootImpl。这个ViewRootImpl是每次WindowManager.addView都会创建一个,apk主界面也会有个。
所以这么看一个apk中,主界面和每次addView都会有IGBP,surfaceView也会对应IGBP。
五 我们问题泄漏的情况
从第四部分看,apk中的addView和主界面有IGBP,主界面维护由framework维护,addView只有对应的removeView, 也会有framework去维护IGBP的引用。
从SurfaceView看的话,每次surface创建成功后会notify onSurfaceCreated,每次surface destroy会notify surfaceDestroyed。这样会通知给apk SurfaceHolder。SurfaceHolder中维护了surface java 端引用。
从我们的逻辑中,有从SurfaceHolder中拿到surface 给jni端使用,这样jni端会拿到java surface对应的cpp surface。
仔细check,发现jni拿的过程中有使用incStrong,导致cpp surface的强引用计数又加了1.这样就导致surfaceDestroyed后,java side 将对应的cpp surface 释放了,cpp surface 的强引用计数减一。但jni端的计数未减掉,这样cpp 的surface泄漏了,导致IGBP也被泄漏了。
如下面代码,ANativeWindow_fromSurface 进行了incStrong,就要调用ANativeWindow_release 主动decStrong。
ANativeWindow* ANativeWindow_fromSurface(JNIEnv* env, jobject surface) {
sp<ANativeWindow> win = android_view_Surface_getNativeWindow(env, surface);
if (win != NULL) {
win->incStrong((void*)ANativeWindow_fromSurface);
}
return win.get();
}
void ANativeWindow_release(ANativeWindow* window) {
// incStrong/decStrong token must be the same, doesn't matter what it is
window->decStrong((void*)ANativeWindow_acquire);
}
六 IGBP释放的逻辑
IGBP的强引用计数分别是cpp的Surface和SurfaceControl给的。所以IGBP的释放需要cpp的Surface和SurfaceControl去释放。
前文提到,cpp的Surface和SurfaceControl强引用计数与java side的Surface/SurfaceControl nativeCreate时给的,并将指针reinterpret_cast给long型存在内部变量中。
在Surface、SurfaceControl的jni code中,也能找到这个计数的decStrong,函数是在nativeRelease中。
static void nativeRelease(JNIEnv* env, jclass clazz, jlong nativeObject) {
sp<SurfaceControl> ctrl(reinterpret_cast<SurfaceControl *>(nativeObject));
ctrl->decStrong((void *)nativeCreate);
}
static void nativeRelease(JNIEnv* env, jclass clazz, jlong nativeObject) {
sp<Surface> sur(reinterpret_cast<Surface *>(nativeObject));
sur->decStrong(&sRefBaseOwner);
}
SurfaceControl的java side 是finalize中有nativeRealse,所以GC时会自动释放掉。
protected void finalize() throws Throwable {
try {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
if (mNativeObject != 0) {
nativeRelease(mNativeObject);
}
} finally {
super.finalize();
}
}
Surface同样也实现了finalize,所以GC时会自动释放掉当前绑定的一个surface。
上文也提到了Surface的copyFrom函数,这边获取到新的cpp Surface,会自动释放掉旧cpp surface。
@Override
protected void finalize() throws Throwable {
try {
if (mCloseGuard != null) {
mCloseGuard.warnIfOpen();
}
release();
} finally {
super.finalize();
}
}
所以这样看,android 原生code 自身不会发生IGBP泄漏的情况,所以发生问题后,大家首先还是先确认是否为自己的问题。