Java调用opencv内存泄漏_OpenCV中的内存泄漏检测-程序员宅基地

技术标签: Java调用opencv内存泄漏  

内存泄漏时程序开发中经常遇到的问题. 而且出现内存泄漏很难检测,

但是其导致的结果却是灾难性的. 这里讲一下opencv中内存泄漏检测

的一些技巧.

OpenCV中关于内存管理主要涉及到以下3个函数:

CV_IMPL void  cvSetMemoryManager( CvAllocFunc alloc_func, CvFreeFunc free_func, void* userdata );

CV_IMPL void* cvAlloc( size_t size );

CV_IMPL void  cvFree_( void* ptr );

还有一个对应cvFree_的宏:

#define cvFree(ptr) (cvFree_(*(ptr)), *(ptr)=0)

宏cvFree的用处是在释放ptr指针对应的内存后, 将ptr设置为NULL.

这里我们先做个假设: opencv中所有的内存分配和释放都是通过cvAlloc和cvFree合作完成的.

如果你使用cvAlloc分配一个内存, 然后用delete来是释放内存是错误的(切记)!!!

因此, 如果我们能够跟踪到cvAlloc/cvFree的调用流程, 就可以分析内存泄漏的情况了.

一般情况下, 一个cvAlloc分配的内存最终必然要对应cvFree来释放, 如果cvAlloc/cvFree不是

匹配出现, 那么可以认为出现了内存泄漏.

为此, 我们需要定义自己的内存管理函数, 然后通过cvSetMemoryManager装载到opencv中.

内存管理函数的类型如下:

typedef void* (CV_CDECL *CvAllocFunc)(size_t size, void* userdata);

typedef int (CV_CDECL *CvFreeFunc)(void* pptr, void* userdata);

其中的userdata是用户通过cvSetMemoryManager来设置的. 我们可以简单的吧userdata当作一个

容器指针, 在每次执行我们自己的alloc_func/free_func函数时, 将内存的分配/释放情况记录到

userdata对应的容器.

为此, 我自己简单设计了一个MemTracker类:

#ifndef OPENCV_MEM_TRACKER_H

#define OPENCV_MEM_TRACKER_H

#include

#include

// 内存泄漏追踪

class MemTracker

{

public:

MemTracker(void);

~MemTracker(void);

private:

// 登记分配/释放的内存

void regAlloc(void *ptr, size_t size);

void regFree(void *ptr);

// 输出泄漏的内存

int output(FILE* fp=stderr);

private:

// 分配内存

static void* alloc_func(size_t size, void *userdata);

// 释放内存

static int free_func(void *ptr, void *userdata);

private:

struct Ptr

{

void *ptr;      // 内存地址

size_t size;   // 内存大小

Ptr(void *ptr, size_t size)

{

this->ptr = ptr;

this->size = size;

}

};

// 记录当前使用中的内存

std::vector   m_memTracker;

};

#endif   // OPENCV_MEM_TRACKER_H

类的实现如下:

#include "MemTracker.h"

#include

#include

MemTracker::MemTracker(void)

{

// 注册管理函数

cvSetMemoryManager(alloc_func, free_func, (void*)this);

}

MemTracker::~MemTracker(void)

{

// 取消管理函数

cvSetMemoryManager(NULL, NULL, NULL);

// 输出结果

this->output();

}

// 登记分配/释放的内存

void MemTracker::regAlloc(void *ptr, size_t size)

{

m_memTracker.push_back(Ptr(ptr, size));

}

void MemTracker::regFree(void *ptr)

{

int i;

for(i = 0; i < m_memTracker.size(); ++i)

{

// 删除记录

if(m_memTracker[i].ptr == ptr)

{

m_memTracker[i] = m_memTracker[m_memTracker.size()-1];

m_memTracker.pop_back();

return;

}

}

}

// 输出泄漏的内存

int MemTracker::output(FILE* fp)

{

int n = m_memTracker.size();

int i;

for(i = 0; i < n; ++i)

{

fprintf(fp, "%d: %p, %u/n", i, m_memTracker[i].ptr, m_memTracker[i].size);

}

return n;

}

// 分配内存

void* MemTracker::alloc_func(size_t size, void *userdata)

{

assert(size > 0 && userdata != NULL);

// 分配内存

void *ptr = malloc(size);

if(!ptr) return NULL;

// 登记

MemTracker *tracker = (MemTracker*)userdata;

tracker->regAlloc(ptr, size);

//

return ptr;

}

// 释放内存

int MemTracker::free_func(void *ptr, void *userdata)

{

assert(ptr != NULL && userdata != NULL);

// 释放内存

free(ptr);

// 登记

MemTracker *tracker = (MemTracker*)userdata;

tracker->regFree(ptr);

// CV_OK == 0

return 0;

}

MemTracker在构造的时候会注册自己的内存管理函数, 在析构的时候会输出没有被释放的内存.

下面我们编写一个测试程序:

#include

#include

#include "MemTracker.h"

int main()

{

MemTracker mem;

IplImage *img = cvLoadImage("lena.jpg", 1);

if(!img) return -1;

// 没有释放img内存

// cvReleaseImage(&img);

return 0;

}

在main函数退出的时候mem会被析构, 然后输出内存的泄漏情况. 下面是在我的电脑上测试的结果:

C:/work/vs2005/MemTracker/debug>MemTracker.exe

0: 00C750C0, 112

1: 00D90040, 786432

OK, 先说到这里吧, 下次再补充...

前面我们已经解决了内存泄漏的检测, 但是在出现内存泄漏的时候我们怎么才能

跟踪到出现内存泄漏的代码呢? 如果能够调试到没有被释放内存对应的cvAlloc函数就好了.

这个我们可以通过m_memTracker[i].ptr来比较内存的地址来检测, 例如在alloc_func中

添加以下代码, 然后设置断点:

// 检测00C750C0内存

if(ptr == (void*)00C750C0)

{

// 设置断点

}

但是这个方法可能还有缺陷. 因为每次运行程序的时候, 内存的布局可能是有区别的.

最好的方法是把cvAlloc的调用顺序记录下来.

变动的部分代码:

class MemTracker

{

struct Ptr

{

void *ptr;      // 内存地址

size_t size;   // 内存大小

int   id;

Ptr(void *ptr, size_t size, int id)

{

this->ptr = ptr;

this->size = size;

this->id = id;

}

};

// 记录当前使用中的内存

std::vector   m_memTracker;

// alloc_func对应的编号

int               m_id;

};

MemTracker::MemTracker(void)

{

m_id = 0;

// 注册管理函数

cvSetMemoryManager(alloc_func, free_func, (void*)this);

}

void MemTracker::regAlloc(void *ptr, size_t size)

{

// 每次记录一个新的m_id

m_memTracker.push_back(Ptr(ptr, size, m_id++));

}

// 输出泄漏的内存

int MemTracker::output(FILE* fp)

{

int n = m_memTracker.size();

int i;

for(i = 0; i < n; ++i)

{

fprintf(fp, "%d: %p, %u/n", m_memTracker[i].id, m_memTracker[i].ptr, m_memTracker[i].size);

}

return n;

}

以后就可以根据m_memTracker[i].id来设置断点跟踪调试. 因为每次运行程序的时候, cvAlloc的调用次序是不变

的, 因此可以认为每次cvAlloc对应的id也是不变的. 这样就可以根据id来追踪出现内存泄漏的cvAlloc了.

对于"OpenCV扩展库", 可以将MemTracker直接集成到CvxApplication中, 这样就可以默认进行内存泄漏检测了.

内存检测先说到这里, 下一节我会简要分析一下OpenCV的cvAlloc等源代码 :D

前面的帖子中我们已经讨论了cvAlloc/cvFree_/cvSetMemoryManager等函数的使用技巧.

下面开始分析OpenCV中以上函数的实现代码. 我觉得如果在阅读代码之前, 如果能对函数的

用法有个基本的认识, 那么对于分析源代码是很有帮助的.

CV_IMPL  void*  cvAlloc( size_t size )

{

void* ptr = 0;

CV_FUNCNAME( "cvAlloc" );

__BEGIN__;

if( (size_t)size > CV_MAX_ALLOC_SIZE )

CV_ERROR( CV_StsOutOfRange,

"Negative or too large argument of cvAlloc function" );

ptr = p_cvAlloc( size, p_cvAllocUserData );

if( !ptr )

CV_ERROR( CV_StsNoMem, "Out of memory" );

__END__;

return ptr;

}

从代码我们可以直观的看出, cvAlloc分配的内存不得大于CV_MAX_ALLOC_SIZE, 即使是使用我们

自己的内存管理函数也会有这个限制.

然后通过p_cvAlloc对应的函数指针对应的函数来分配内存. p_cvAlloc是一个全局static变量, 对应的

还有p_cvFree和p_cvAllocUserData, 分别对应释放内存函数和用户数据. 它们的定义如下:

// 指向分配函数的指针,初始设置为默认值

static CvAllocFunc p_cvAlloc = icvDefaultAlloc;

static CvFreeFunc p_cvFree = icvDefaultFree;

static void* p_cvAllocUserData = 0;

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/weixin_42517317/article/details/114963433

智能推荐

874计算机科学基础综合,2018年四川大学874计算机科学专业基础综合之计算机操作系统考研仿真模拟五套题...-程序员宅基地

文章浏览阅读1.1k次。一、选择题1. 串行接口是指( )。A. 接口与系统总线之间串行传送,接口与I/0设备之间串行传送B. 接口与系统总线之间串行传送,接口与1/0设备之间并行传送C. 接口与系统总线之间并行传送,接口与I/0设备之间串行传送D. 接口与系统总线之间并行传送,接口与I/0设备之间并行传送【答案】C2. 最容易造成很多小碎片的可变分区分配算法是( )。A. 首次适应算法B. 最佳适应算法..._874 计算机科学专业基础综合题型

XShell连接失败:Could not connect to '192.168.191.128' (port 22): Connection failed._could not connect to '192.168.17.128' (port 22): c-程序员宅基地

文章浏览阅读9.7k次,点赞5次,收藏15次。连接xshell失败,报错如下图,怎么解决呢。1、通过ps -e|grep ssh命令判断是否安装ssh服务2、如果只有客户端安装了,服务器没有安装,则需要安装ssh服务器,命令:apt-get install openssh-server3、安装成功之后,启动ssh服务,命令:/etc/init.d/ssh start4、通过ps -e|grep ssh命令再次判断是否正确启动..._could not connect to '192.168.17.128' (port 22): connection failed.

杰理之KeyPage【篇】_杰理 空白芯片 烧入key文件-程序员宅基地

文章浏览阅读209次。00000000_杰理 空白芯片 烧入key文件

一文读懂ChatGPT,满足你对chatGPT的好奇心_引发对chatgpt兴趣的表述-程序员宅基地

文章浏览阅读475次。2023年初,“ChatGPT”一词在社交媒体上引起了热议,人们纷纷探讨它的本质和对社会的影响。就连央视新闻也对此进行了报道。作为新传专业的前沿人士,我们当然不能忽视这一热点。本文将全面解析ChatGPT,打开“技术黑箱”,探讨它对新闻与传播领域的影响。_引发对chatgpt兴趣的表述

中文字符频率统计python_用Python数据分析方法进行汉字声调频率统计分析-程序员宅基地

文章浏览阅读259次。用Python数据分析方法进行汉字声调频率统计分析木合塔尔·沙地克;布合力齐姑丽·瓦斯力【期刊名称】《电脑知识与技术》【年(卷),期】2017(013)035【摘要】该文首先用Python程序,自动获取基本汉字字符集中的所有汉字,然后用汉字拼音转换工具pypinyin把所有汉字转换成拼音,最后根据所有汉字的拼音声调,统计并可视化拼音声调的占比.【总页数】2页(13-14)【关键词】数据分析;数据可..._汉字声调频率统计

linux输出信息调试信息重定向-程序员宅基地

文章浏览阅读64次。最近在做一个android系统移植的项目,所使用的开发板com1是调试串口,就是说会有uboot和kernel的调试信息打印在com1上(ttySAC0)。因为后期要使用ttySAC0作为上层应用通信串口,所以要把所有的调试信息都给去掉。参考网上的几篇文章,自己做了如下修改,终于把调试信息重定向到ttySAC1上了,在这做下记录。参考文章有:http://blog.csdn.net/longt..._嵌入式rootfs 输出重定向到/dev/console

随便推点

uniapp 引入iconfont图标库彩色symbol教程_uniapp symbol图标-程序员宅基地

文章浏览阅读1.2k次,点赞4次,收藏12次。1,先去iconfont登录,然后选择图标加入购物车 2,点击又上角车车添加进入项目我的项目中就会出现选择的图标 3,点击下载至本地,然后解压文件夹,然后切换到uniapp打开终端运行注:要保证自己电脑有安装node(没有安装node可以去官网下载Node.js 中文网)npm i -g iconfont-tools(mac用户失败的话在前面加个sudo,password就是自己的开机密码吧)4,终端切换到上面解压的文件夹里面,运行iconfont-tools 这些可以默认也可以自己命名(我是自己命名的_uniapp symbol图标

C、C++ 对于char*和char[]的理解_c++ char*-程序员宅基地

文章浏览阅读1.2w次,点赞25次,收藏192次。char*和char[]都是指针,指向第一个字符所在的地址,但char*是常量的指针,char[]是指针的常量_c++ char*

Sublime Text2 使用教程-程序员宅基地

文章浏览阅读930次。代码编辑器或者文本编辑器,对于程序员来说,就像剑与战士一样,谁都想拥有一把可以随心驾驭且锋利无比的宝剑,而每一位程序员,同样会去追求最适合自己的强大、灵活的编辑器,相信你和我一样,都不会例外。我用过的编辑器不少,真不少~ 但却没有哪款让我特别心仪的,直到我遇到了 Sublime Text 2 !如果说“神器”是我能给予一款软件最高的评价,那么我很乐意为它封上这么一个称号。它小巧绿色且速度非

对10个整数进行按照从小到大的顺序排序用选择法和冒泡排序_对十个数进行大小排序java-程序员宅基地

文章浏览阅读4.1k次。一、选择法这是每一个数出来跟后面所有的进行比较。2.冒泡排序法,是两个相邻的进行对比。_对十个数进行大小排序java

物联网开发笔记——使用网络调试助手连接阿里云物联网平台(基于MQTT协议)_网络调试助手连接阿里云连不上-程序员宅基地

文章浏览阅读2.9k次。物联网开发笔记——使用网络调试助手连接阿里云物联网平台(基于MQTT协议)其实作者本意是使用4G模块来实现与阿里云物联网平台的连接过程,但是由于自己用的4G模块自身的限制,使得阿里云连接总是无法建立,已经联系客服返厂检修了,于是我在此使用网络调试助手来演示如何与阿里云物联网平台建立连接。一.准备工作1.MQTT协议说明文档(3.1.1版本)2.网络调试助手(可使用域名与服务器建立连接)PS:与阿里云建立连解释,最好使用域名来完成连接过程,而不是使用IP号。这里我跟阿里云的售后工程师咨询过,表示对应_网络调试助手连接阿里云连不上

<<<零基础C++速成>>>_无c语言基础c++期末速成-程序员宅基地

文章浏览阅读544次,点赞5次,收藏6次。运算符与表达式任何高级程序设计语言中,表达式都是最基本的组成部分,可以说C++中的大部分语句都是由表达式构成的。_无c语言基础c++期末速成