通过cgo从Go传递OpenCV Mat指针到C++重建时内存泄漏如何解决
问题根本原因
你的内存泄漏核心来自两个常见错误点:
- 使用外部数据指针构造
cv::Mat时未传入正确的行步长(step)参数,导致Mat内存大小计算错误,拷贝、析构时出现内存异常 - 你用到的
clone()/copyTo()生成的新Mat对象如果被后续TensorFlow推理逻辑隐式持有所有权,没有随函数执行结束释放,就会出现泄漏
修复方案
方案1:无拷贝方案(性能最优,无内存泄漏)
该方案下C侧的cv::Mat完全不持有数据内存所有权,所有内存由Go侧gocv.Mat管理,C侧仅做访问使用:
Go侧补全传递参数
除了数据指针,你需要将Mat的宽、高、类型、行步长都同步传到C++:
func Detect(detectFrame *gocv.Mat) { detectFrameSlice_ptr, _ := detectFrame.DataPtrUint8() detectFrame_ptr := unsafe.Pointer(&detectFrameSlice_ptr[0]) // 新增传递参数 width := detectFrame.Cols() height := detectFrame.Rows() matType := detectFrame.Type() step := detectFrame.Step() C.LIB_predict(detectFrame_ptr, C.int(width), C.int(height), C.int(matType), C.size_t(step)) // Go侧只要保证detectFrame在LIB_predict执行完成前不被Close/GC回收即可 // ... }
C++侧构造无所有权的Mat
用完整参数构造Mat,不需要拷贝数据,函数结束后Mat自动析构,不会释放外部传入的指针内存:
// 先修改头文件声明 void LIB_predict(void* detectFrame_ptr, int width, int height, int matType, size_t step); void LIB_predict(void* detectFrame_ptr, int width, int height, int matType, size_t step){ uchar* ptr = static_cast<uchar*>(detectFrame_ptr); // 传入step参数,构造的Mat不持有ptr的所有权,析构时不会释放ptr cv::Mat inputMat(height, width, matType, ptr, step); // 后续推理逻辑直接用inputMat即可,不需要clone/copyTo // 函数结束时inputMat作为局部变量自动析构,无内存泄漏 }
方案2:拷贝数据方案(适合C++侧需要长期持有Mat数据的场景)
如果你需要在C++侧保留数据超过本次函数调用的生命周期,就做一次安全拷贝:
void LIB_predict(void* detectFrame_ptr, int width, int height, int matType, size_t step){ uchar* ptr = static_cast<uchar*>(detectFrame_ptr); // 先构造临时无所有权的Mat,再做深拷贝 cv::Mat tempMat(height, width, matType, ptr, step); cv::Mat inputMat = tempMat.clone(); // tempMat会在构造完inputMat后自动析构,inputMat持有自己的内存 // 如果你后续把inputMat转Tensor,确保Tensor的生命周期不超过inputMat的生命周期即可 // 不需要长期持有inputMat的情况下,函数结束后自动析构,不会泄漏 }
额外排查点
如果用了上述方案还是有泄漏,检查两个地方:
- Go侧的
detectFrame是不是调用完Detect函数后没有显式调用Close()方法:gocv的Mat底层是C++分配的内存,不会被Go GC自动回收,必须手动调用Close()释放 - 你转Tensor的逻辑是不是直接持有了
inputMat.data的指针,没有在Tensor销毁时释放对应的内存
内容的提问来源于stack exchange,提问作者eirikaso
相关产品推荐
相关产品推荐

