【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
在我们个人编程的过程当中,内存泄露虽然不会像内存溢出那样造成各种莫名奇妙的问题,但是它的危害也是不可忽视的。一方面,内存的泄露导致我们的软件在运行过程中占用了越来越多的内存,占有资源而又得不到及时清理,这会导致我们程序的效率越来越低;另一方面,它会影响我们用户的体验,失去市场的竞争能力。
常见的内存泄露是这样的:
void process(int size)
{
char* pData = (char*)malloc(size);
/* other code */
return; /* forget to free pData */
}
如上图所示,我们在函数process的处理过程中,每一次都需要对内存进行申请,但是在函数结束的时候却没有进行释放。如果这样的一段代码出现在业务侧,那么后果是难以想象的。举个例子来说,如果我们服务器每秒钟需要接受100个用户的并发访问,每个用户过来的数据,我们都需要本地申请内存重新保存一份。处理结束之后,如果内存没有得到很好地释放,就会导致我们服务器可用的物理内存越来越少。一旦达到某一个临界点之后,操作系统不得不通过内外存的调度来满足我们申请新内存的需求,这在另一方面来讲又会降低服务器服务的质量。
内存泄露的危害是不言而喻的,但是查找内存泄露却是一件苦难而且复杂的工作。我们都知道,解决bug是一件非常简单的事情,但是寻找bug的出处却是一件非常吃力的事情。因此,我们有必要在自己编写代码的时候,就把查找内存泄露的工作放在很重要的位置上面。那么有没有什么办法来解决这一问题呢?
我想要做到解决内存泄露,必须做到下面两个方面:
(1)必须记录内存在哪个函数申请的,具体文件的行数是多少
(2)内存应该什么时候被释放
要完成第1个条件其实并不困难。我们可以用节点的方法记录我们申请的内存:
a)设置节点的数据结构
typedef struct _MEMORY_NODE
{
char functionName[64];
int line;
void* pAddress;
struct _MEMORY_NODE* next;
}MEMORY_NODE;
其中 functionName记录函数名称,line记录行数, pAddress记录分配的地址, next记录下一个内存节点。
b)修改内存的分配函数
对业务侧的malloc进行函数修改,添加下面一句宏语句
#define malloc(param) MemoryMalloc(__FUNCTION__, __LINE__, param)
在桩函数侧书写下面的代码
void* MemoryMalloc(const char* name, int line, int size)
{
void* pData = (void*)malloc(size);
MEMORY_NODE* pMemNode = NULL;
if(NULL == pData) return NULL;
memset((char*)pData, 0, size);
pMemNode = (MEMORY_NODE*)malloc(sizeof(MEMORY_NODE));
if(NULL == pMemNode){
free(pData);
return NULL;
}
memset((char*)pMemNode, 0, sizeof(MEMORY_NODE));
memmove(pMemNode->functionName, name, strlen(name));
pMemNode->line = line;
pMemNode->pAddress = pData;
pMemNode->next = NULL;
add_memory_node(pMemNode);
return pData;
}
内存的分配过程中还涉及到了节点的添加,所以我们还需要添加下面的代码
static MEMORY_NODE* gMemNode = NULL;
void add_memory_node(MEMORY_NODE* pMemNode)
{
MEMORY_NODE* pNode = gMemNode;
if(NULL == pMemNode) return;
if(NULL == gMemNode){
gMemNode = pMemNode;
return;
}
while(NULL != pNode->next){
pNode = pNode->next;
}
pNode->next = pMemNode;
return;
}
文中gMemNode表示所有内存节点的根节点,我们每增加一次malloc过程就会对内存节点进行记录。在记录过程中,我们还会记录调用malloc的函数名称和具体文件行数,这主要是为了方便我们在后面进行故障定位的时候更好地查找。
完成了第一个条件之后,我们就要对第二个条件进行完成。
a)内存什么时候释放,这取决于我们在函数中是怎么实现的,但是我们在编写测试用例的时候却是应该知道内存释放没有,比如说如果测试用例全部结束了,我们有理由相信assert(gMemNode
== NULL)这应该是恒等于真的。
b)内存释放的时候,我们应该做些什么?和节点的添加一样,我们在内存释放的时候需要free指定的内存,free节点,free节点的内存,下面就是在释放的时候我们需要进行的操作
对业务侧的free函数进行修改,添加下面一句宏代码,
#define free(param) MemoryFree(param)
在桩函数侧输入下面的代码:
void MemoryFree(void* pAddress)
{
if(NULL == pAddress) return;
delete_memory_node(pAddress);
free(pAddress);
}
在删除内存的时候,需要删除节点,删除节点的内存
void delete_memory_node(void* pAddress)
{
MEMORY_NODE* pHead = gMemNode;
MEMORY_NODE* pMemNode = gMemNode;
while(NULL != pMemNode){
if(pAddress == pMemNode->pAddress)
break;
pMemNode = pMemNode->next;
}
if(NULL == pMemNode) {
assert(1 == 0);
return;
}
while(pMemNode != pHead->next){
pHead = pHead->next;
}
if(pMemNode == gMemNode){
gMemNode = gMemNode->next;
}else{
pHead->next = pMemNode->next;
}
free(pMemNode);
return;
}
有了上面一小段代码的帮助,我们在编写测试用例的时候,就可以在函数执行后,通过判断内存节点是否为空的方法判断内存是否已经释放。如果内存没有释放,我们还能通过节点的信息帮助我们是哪里发生了错误,但是这个方法还有两个缺点:
(1)没有考虑缓存的情况,好多内存分配了之后并不会在函数中马上释放,而是放在缓存池中等待下一次调用,这就需要我们准确把握和判断了。
(2)代码中节点删除和添加的时候没有考虑多进程的情形,应该考虑用一个互斥锁或者是信号量加以保护。
(全文完)
分享到:
相关推荐
Android处理内存泄漏的代码例子。用于演示避免内存泄漏的几种方法,包括:关闭游标、重用适配、回收图像、注销监听、释放引用。
用于c++程序内存泄漏检查的代码,嵌入到你的工程中,程序退出的时候可以看到那些内存没有回收。厉害吧。转IBM。有新版,暂时不放上了。...内存泄露情况在程序退出时控制台输出。建议搜索一下里面文章的标题。
一个PHPUnit插件,可以检测经过测试的代码或测试中的内存泄漏
用于freertos嵌入式实时系统内存泄漏检测,可以实时查看内存状况,具体平台是ESP32
mfc伪内存泄漏测试代码,关键是在你的exe里面,手动添加mfc80ud.lib(根据具体版本自己修改)
摘 要 内存泄漏故障是程序中某处申请的内存空间,没有释放或没有完全释放或多次释放,是程序中常见的故障, 极易导致系统崩溃。从面向具体错误的测试思想出发,...关键词 内存泄露,区间运算,静态测试,故障模型,别名分析
opencv3和opencv4多线程内存泄漏问题:以cv::resize函数测试结果为例。 使用中可修复或者可避免内存泄漏:1)使用opencv2的版本;2)在代码中设置修复该问题.
可以检测代码中的内存泄漏问题、bug分析,提高程序员效率。
常用的做手机的内存测试代码,代码中对各类的效率监管很有用
【2】其中包含5种内存泄漏情况的C++代码。分别是 1.使用野指针,即未初始化的指针; 2.释放野指针,即未初始化的指针; 3. 动态内存越界; 4. 堆内存泄漏,没有成对使用malloc/free和new/delete; 5.两次释放内存;
小巧实用的内存测试的小工具,内涵C#源码
内存泄漏是非常常见的问题,当以前分配的一片内存不再需要使用或无法访问时,但是却并没有释放它,那么对于该进程来说,会因此导致总可用...本资源能有效的辅助检测和调试程序中的内存泄露问题,为国外开源社区代码。
谷歌测试内存泄漏监听器 在 Microsoft Visual Studio 中对 Google Test 进行简单的内存泄漏检查。 使用 Microsoft 内存检查 CRT 工具。 请参阅: : 谷歌测试字符串错误格式 增强字符串匹配失败消息,以帮助处理大...
测试JavaScript在IE中的内存泄露,很好用的一个工具
2.当程序出现内存泄露时,可以快速定位到是哪个文件哪行出现问题,如果代码在几万行以上,这点非常重要。 3.由于MT4与MT5编译器不具备查找内存泄漏的能力,所有我们只能自己实现而且越早发现越好。 四、检测类型泄露...
一款公司内部使用基于windows下UMDH工具进行栈追踪,进而具体定位程序内存泄漏代码行的极简工具。适合测试、维护、售后技术及开发人员使用。快速定位代码行避免重复输入命令,自动生成分析报表,提高维护效率。目前...
roave / no-leaks该库是一个PHPUnit插件,可以检测经过测试的代码或测试中的内存泄漏。 安装作曲家需要--dev roave / no-leaks用法在phpunit.xml配置中,添加followi roave / no-leaks这个库是一个PHPUnit插件,可以...
全文通过与C++中的内存泄漏问题进行对比,讲述了Java内存泄漏的基本原理,以及如何借助Optimizeitprofiler工具来测试内存泄漏和分析内存泄漏的原因,在实践中证明这是一套行之有效的方法。问题的提出笔者曾经参与...
mmleak测试代码,主要用作Android来测试内存泄漏的复现效果。
但我今天换了种测试方法,貌似不打开 devtools 时 console.log 是没有内存泄漏的。 不打开 devtools 怎么确定内存泄漏问题呢? 看下内存大小不就知道了? 通过 performance.memory.totalJSHeapSize 是可以拿到堆...