“坑”描述:
在对我们自己研发的一款android终端进行camera拍照压力测试时,发现当拍照张数达到几万张时,查看内存占用情况,发现内存泄露。
填“坑”:
frameworks/base/core/jni/android/graphics/YuvToJpegEncoder.cpp
bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width,
int height, int* offsets, int jpegQuality) {
jpeg_compress_struct cinfo;
skjpeg_error_mgr sk_err;
skjpeg_destination_mgr sk_wstream(stream);
cinfo.err = jpeg_std_error(&sk_err);
sk_err.error_exit = skjpeg_error_exit;
if (setjmp(sk_err.fJmpBuf)) {
return false;
}
jpeg_create_compress(&cinfo);
cinfo.dest = &sk_wstream;
setJpegCompressStruct(&cinfo, width, height, jpegQuality);
jpeg_start_compress(&cinfo, TRUE);
compress(&cinfo, (uint8_t*) inYuv, offsets);
jpeg_finish_compress(&cinfo);
return true;
}
坑就在上面这个接口函数中:
熟悉libjpeg的同学会注意到,上面的接口在调用完jpeg_finish_compress()后,没有调用jpeg_destroy_compress(),这个接口是释放压缩工作过程中所申请的资源,主要就是jpeg压缩对象。
由于android原生接口中,没有调用jpeg_destroy_compress()导致每次泄露几十个字节,当拍照数量达到万级时,才会有所察觉。
怎么找到这个坑的:
这个过程后面有时间会详细写下,目前心得就是模块的架构十分重要,对这种数据流的控制,pipeline方式是比较好的方案,因为可以明确输入输出,然后通过伪造输入输出对各个模块进行单独的压力测试。最难控制的就是“洋葱”式的包裹调用,要像“剥洋葱”一样一层层的剥离十分麻烦。
你的android机上有这个问题吗:
9成的概率下你的手机应该不会有这个问题,因为上面我讲到是在我们做的一款终端上发现的问题,我们的终端芯片方案比较挫,没有硬编码模块,导致使用了android的软编码方案,也就用到libjpeg这个模块,也就触发了上面问题函数接口的调用。
牢骚:
做底层系统开发就是这样,一个bug耗费了很久的时间去测试,查找,验证。一层层剥离模块,逐步定位问题的大概位置,到最后精确定位问题,并解决,bug的解决可能就是一行代码的事(上面就加上destroy接口即可),但着实耗费了不少时间,如果按照代码行数计算kpi,这个performance应该是差的可以了。