GraphicBufferProducer的引用关系

本文详细探讨了SurfaceFlinger中IGraphicBufferProducer的创建、销毁过程以及在压测中出现crash的原因,重点聚焦于IGBP的创建与销毁逻辑,以及客户端与服务器端的交互。通过深入分析,揭示了内存泄漏的可能来源和解决策略。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近碰到一个问题:压测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泄漏的情况,所以发生问题后,大家首先还是先确认是否为自己的问题。

评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值