Java调用opencv内存泄漏_OpenCV中的内存泄漏检测

本文探讨了在Java调用OpenCV时如何检测内存泄漏,重点介绍了OpenCV的内存管理函数cvAlloc和cvFree,并提供了一个名为MemTracker的类,用于追踪内存分配和释放,以检测内存泄漏。通过实例展示了如何使用MemTracker在程序运行时记录内存使用情况,并在内存泄漏发生时进行调试。
摘要由CSDN通过智能技术生成

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

但是其导致的结果却是灾难性的. 这里讲一下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;

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值