最近在使用opencv的时候突然发现经过一段时间,就会报出 opencv error: insufficient memory 的问题,我反复检查代码,也没发现问题。 (事后证明这只是粗心而已)后来在拜读网上各个论坛的意见,并且尝试后,终于解决了问题,但却并不仅仅是代码或者工程设置上变化。
首先必须澄清的是,这种错误也许有很多可能,可能是因为编译不是用的 x64, 可能是因为图形太大造成的,也可能在 Linker 的 system 下修改允许大内存地址就能解决等等。在这之前还是先假设为代码会出错吧,因为代码出错的可能性总是多种多样的。
因为我的编译系统是 VS2015,也就暂时以此为根据吧。为了解决内存不够的问题,我查看了 VS2015 的 diagnostic tools,里面有 Process Memory 和 CPU 的用量观察。反复几次观察后,我发现我的内存在大概3分钟后升高到 3.3GB并崩溃。于是我使用了工具中的 take snapshot,会得到一个各个线程和变量使用堆栈资源的列表。
之后进入图片下部的这个Allocations或者Heap Size,进入后都是一样的,可以选择按照变量类型或者按照堆栈来看内容。
在堆栈图可以直接看到自己写的函数和变量,就对着Size最大的去就行了,因为既然是内存不足,肯定是撑爆了内存的家伙的错啊!但是要说的是,这样并不能直接精确地找到哪里出错,而是只能找到哪个函数出了错误。不过万幸的是,找哪个变量出错,不需要太麻烦,只要多看看 new 了些什么变量就行了。
我因为在无限循环里 new 了个unsigned char* 的矩阵,却没有把它在函数中delete掉,所以最后就被撑爆了。但是因为这是个返回值 unsigned char* 的函数,我没法在return之后再delete,于是写了两行代码作为替代。首先我把要delete的变量设为了全局变量,没初始化的那种。代码大致如下
unsigned char* imageArray; // 设置全局变量
unsigned char* transferImage() // 函数和返回值
{
// 如果变量不为NULL,就清理其内存,并设置为NULL,这一步是为了防止旧内存堆积
if (imageArray != NULL)
{
delete[] imageArray;
imageArray = NULL;
}
imageArray = new unsigned char[rows * cols * channels]; // 清理完后就能够名正言顺的生成新内存了
unsigned char* row_buffer = imageArray;
unsigned char* src_buffer = imageToCopy.ptr();
// 将opencv的Mat图像内容copy进新的内存
memcpy(row_buffer, src_buffer, imageToCopy.cols * imageToCopy.rows* imageToCopy.elemSize1()* imageToCopy.channels());
return imageArray; // 返回图像指针
}
这么一弄之后,3.3GB的内存用量一下子下降到200MB,说明问题已经解决了。
这个方法可以反复查看内存的溢出。