Android 图片压缩内存泄漏问题
最近在做 Android 相关的 YUV 图像压缩处理时,发现某些设备有内存泄露问题,这里作个简单梳理。
libjpeg
开源的 JPG 编解码库主要是 libjpeg,其他的诸如 libjpeg-turbo、mozjpeg 也是在它基础上 fork 的版本,增加了 SIMD 等支持,并且 API 也是兼容的。
libjpeg 压缩的核心流程如下:
jpeg_create_compress(): 初始化;
jpeg_stdio_dest(): 设置压缩后保存的目标;
jpeg_set_defaults(): 设置压缩参数;
jpeg_set_quality(): 设置压缩质量;
jpeg_start_compress(): 开始压缩循环;
jpeg_write_scanlines(): 写入数据;
jpeg_finish_compress(): 数据写完后调用,结束压缩循环;
jpeg_destroy_compress(): 释放资源。
下面我们从 Android 的相关源码着手分析。
YuvImage 的压缩
YuvImage.compressToJpeg()
实际上调用的是 YuvToJpegEncoder
。
但是,Android 8.0 的实现居然没释放资源:
bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, int height, int* offsets, int jpegQuality) {
...
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;
}
Android 9.0 的实现才补上了资源释放逻辑:
bool YuvToJpegEncoder::encode(SkWStream* stream, void* inYuv, int width, int height, int* offsets, int jpegQuality) {
...
if (setjmp(err.jmp)) {
jpeg_destroy_compress(&cinfo);
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);
jpeg_destroy_compress(&cinfo);
return true;
}
Bitmap 的压缩
但是,我们之前 Bitmap.compress()
用了这么多年也没发现内存泄漏。
同样分析源码,发现最终调用的是 SkImageDecoder_libjpeg.cpp
。
我们找个比较老的 5.0 的源码看下:
virtual bool onEncode(SkWStream* stream, const SkBitmap& bm, int quality) {
...
jpeg_create_compress(&cinfo);
...
jpeg_set_defaults(&cinfo);
jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
...
jpeg_start_compress(&cinfo, TRUE);
...
while (cinfo.next_scanline < cinfo.image_height) {
...
(void) jpeg_write_scanlines(&cinfo, row_pointer, 1);
...
}
jpeg_finish_compress(&cinfo);
jpeg_destroy_compress(&cinfo);
return true;
}
可以看到他最后也是有释放资源,所以没有内存泄露问题。