现象描述:
在launcher的使用过程中,会发现系统越来越慢,内存越来越少,通过adb shell ps -t可以查看到当前launcher开启了几百上千个线程,这样就导致了系统越来越慢。
分析:
通过定位,发现在hwui中,当绘制带阴影的字体的时候,使用了renderScript,由于renderScript中绘制阴影时,会开启3-4个线程来处理阴影文字。
void FontRenderer::blurImage(uint8_t** image, int32_t width, int32_t height, int32_t radius) {
#ifdef ANDROID_ENABLE_RENDERSCRIPT
if (width * height * radius >= RS_MIN_INPUT_CUTOFF) {
uint8_t* outImage = (uint8_t*) memalign(RS_CPU_ALLOCATION_ALIGNMENT, width * height);
if (mRs == 0) {
mRs = new RSC::RS();
if (!mRs->init(RSC::RS_INIT_LOW_LATENCY | RSC::RS_INIT_SYNCHRONOUS)) {
ALOGE("blur RS failed to init");
}
mRsElement = RSC::Element::A_8(mRs);
mRsScript = RSC::ScriptIntrinsicBlur::create(mRs, mRsElement);
}
RSC::sp t = RSC::Type::create(mRs, mRsElement, width, height, 0);
RSC::sp<:allocation> ain = RSC::Allocation::createTyped(mRs, t,
RS_ALLOCATION_MIPMAP_NONE, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
*image);
RSC::sp<:allocation> aout = RSC::Allocation::createTyped(mRs, t,
RS_ALLOCATION_MIPMAP_NONE, RS_ALLOCATION_USAGE_SCRIPT | RS_ALLOCATION_USAGE_SHARED,
outImage);
mRsScript->setRadius(radius);
mRsScript->setInput(ain);
mRsScript->forEach(aout);
// replace the original image's pointer, avoiding a copy back to the original buffer
free(*image);
*image = outImage;
return;
}
#endif当内存不足时,会调用GammaFontRenderer的clear函数,也就是会将当前的FontRenderer对象删除,由于rs对象是以智能指针的形式来使用,所以正常情况在FontRenderer的时候会将当前的rs指针对象都删除,通过写一些测试用例发现,rs使用的智能指针在析构的时候并没有将rs的对象删除,这也就是导致每次创建了rs对象没删除,导致一直会创建大量的线程的原因。
通过查看renderScript的代码,发现rs的对象都是使用强指针引用(sp)的方式来使用:
struct {
sp U8;
sp U8_2;
sp U8_3;
sp U8_4;
…………
sp A_8;
sp RGB_565;
sp RGB_888;
sp RGBA_5551;
sp RGBA_4444;
sp RGBA_8888;
sp YUV;
sp MATRIX_4X4;
sp MATRIX_3X3;
sp MATRIX_2X2;
} mElements;这样导致mRsElement中使用了mRs的强引用,当mRsElement也保存了mRs的强引用,当mRsElement生命周期结束时,由于不等1,所以只能将mRsElement的引用计数减1,mRs还是为2,这样也就会导致最后出现循环引用问题,无法将任何一个对象删除,这就导致了申请的线程没有释放,出现了fb socket泄露的问题。
解决方案:
1. 临时解决方案:
将FontRenderer的rs对象都设置为静态对象,这样就能避免出现socket泄露的问题:
#ifdef ANDROID_ENABLE_RENDERSCRIPT
// RS constructs
static RSC::sp<:rs> mRs;
static RSC::sp mRsElement;
static RSC::sp<:scriptintrinsicblur> mRsScript;
#endif
2. 修改renderScript:
这种修改方式比较麻烦,需要将rs中存在强智能指针的对象都改为普通指针的方式,所幸的是,后面google已经在这方便做了修改,所以可以参考google官网的修改:https://android-review.googlesource.com