Android端OpenCV内存泄漏:垃圾回收无法回收Mat对象问题
OpenCV Mat对象帧处理场景内存泄漏问题解析
问题现象
开发逐帧处理视频的演示应用时,发现函数内部创建的Mat对象在函数执行结束后无法被正常回收,导致内存持续上涨:
class MainActivity ... ... override fun onCameraFrame(inputFrame: CvCameraViewFrame): Mat { val rgbaMat: Mat = inputFrame.rgba()!! val lets_have_a_memory_leak = false if (lets_have_a_memory_leak) { val unused_leaker = Mat.zeros(rgbaMat.size(), rgbaMat.type()) } return rgbaMat }
- 开关
lets_have_a_memory_leak == false时,内存占用稳定无异常:
- 开关
lets_have_a_memory_leak == true时,内存快速持续消耗,直到应用被系统杀死:
目前已验证的现象:
- 将方法内临时使用的Mat预分配为类成员变量复用时,可暂时规避泄漏
- 主动对临时Mat调用
unused_leaker.release(),可完全阻止内存溢出
核心原理
GC没有按预期回收内存不是GC故障,本质是OpenCV Java/Kotlin绑定的内存模型和JVM GC管辖范围不匹配:
- JVM GC的回收范围仅限JVM堆内存,也就是
Mat这个Kotlin/Java对象本身占用的内存,这部分只有几十字节,占比极低 Mat存储像素数据的实际内存是直接在Native堆分配的,完全不在JVM GC的检测、回收范围内- OpenCV的绑定逻辑确实给
Mat写了finalize兜底释放逻辑:只有当JVM堆内存不足、触发GC回收Mat的Java对象时,才会顺带调用native方法释放对应的Native内存。但逐帧处理场景下,每秒会创建数十个临时Mat,每个Mat对应的Java对象占用内存极小,JVM堆内存长期处于充足状态,GC几乎不会被触发,Native堆的像素内存就会无限制累积,最终触发OOM。
最佳实践
- 所有临时创建的
Mat对象,使用完毕后必须主动调用.release()手动释放Native内存,绝对不要依赖finalize机制兜底——finalize执行时机完全不可控,且在高版本Android/JDK中已被废弃。 - 高频帧处理场景下,优先复用预分配的类成员
Mat对象,减少重复分配、释放的性能开销。 - 可通过Kotlin扩展函数实现
use作用域,自动完成释放,避免遗漏:
inline fun <T> Mat.use(block: (Mat) -> T): T { return try { block(this) } finally { release() } } // 调用示例 Mat.zeros(rgbaMat.size(), rgbaMat.type()).use { tempMat -> // 在此处对tempMat做处理 } // 作用域结束自动调用release,即使抛出异常也不会泄漏
内容的提问来源于stack exchange,提问作者Peter
相关产品推荐
相关产品推荐

