linux中,由于使用malloc或alloc而没有free掉申请的内存,就会造成内存的泄露。通常,来讲为了避免内存泄露的情况出现,一般要求,我们尽量的malloc之后,调用free。但是总会有忘记free的时候啊。一般可以有如下几种方式来避免内存泄露:
1) 使用智能指针,这个在C++中较为常见;
2) 使用内存池;
3) 自己封装一层malloc/free等等。当申请内存时,将申请信息放入到一个已分配内存信息链表里面。free时,删除对应的信息表的节点。在
程序执行结束前,扫瞄该信息表,若还存在信息节点,那么该节点记录的就是泄露的内存信息。若链表为空,那就是说没有发生内存泄露;
4)使用检测工具检测内存泄露,进而修补程序,这样的工具有比如Valgrind等等。
以下,给出一个按方式3实现的内存泄露检测的实现版本,
代码量本身不多。
leak_detector_c.h文件
#ifndef LEAK_DETECTOR_C_H
#define LEAK_DETECTOR_C_H
#define FILE_NAME_LENGTH 256
#define OUTPUT_FILE "/home/leak_info.txt" /*this file should be created first!*/
#define malloc(size) xmalloc (size, __FILE__, __LINE__)
#define calloc(elements, size) xcalloc (elements, size, __FILE__, __LINE__)
#define free(mem_ref) xfree(mem_ref)
struct _MEM_INFO
{
void *address;
unsigned int size;
char file_name[FILE_NAME_LENGTH];
unsigned int line;
};
typedef struct _MEM_INFO MEM_INFO;
struct _MEM_LEAK {
MEM_INFO mem_info;
struct _MEM_LEAK * next;
};
typedef struct _MEM_LEAK MEM_LEAK;
void add(MEM_INFO alloc_info);
void erase(unsigned pos);
void clear(void);
void * xmalloc(unsigned int size, const char * file, unsigned int line);
void * xcalloc(unsigned int elements, unsigned int size, const char * file, unsigned int line);
void xfree(void * mem_ref);
void add_mem_info (void * mem_ref, unsigned int size, const char * file, unsigned int line);
void remove_mem_info (void * mem_ref);
void report_mem_leak(void);
#endif
leak_detector_c.c文件
#include
#include
#include
#include "leak_detector_c.h"
#undef malloc
#undef calloc
#undef free
static MEM_LEAK * ptr_start = NULL;
static MEM_LEAK * ptr_next = NULL;
/*
* adds allocated memory info. into the list
*
*/
void add(MEM_INFO alloc_info)
{
MEM_LEAK * mem_leak_info = NULL;
mem_leak_info = (MEM_LEAK *) malloc (sizeof(MEM_LEAK));
mem_leak_info->mem_info.address = alloc_info.address;
mem_leak_info->mem_info.size = alloc_info.size;
strcpy(mem_leak_info->mem_info.file_name, alloc_info.file_name);
mem_leak_info->mem_info.line = alloc_info.line;
mem_leak_info->next = NULL;
if (ptr_start == NULL)
{
ptr_start = mem_leak_info;
ptr_next = ptr_start;
}
else {
ptr_next->next = mem_leak_info;
ptr_next = ptr_next->next;
}
}
/*
* erases memory info. from the list
*
*/
void erase(unsigned pos)
{
unsigned index = 0;
MEM_LEAK * alloc_info, * temp;
if(pos == 0)
{
MEM_LEAK * temp = ptr_start;
ptr_start = ptr_start->next;
free(temp);
}
else
{
for(index = 0, alloc_info = ptr_start; index < pos;
alloc_info = alloc_info->next, ++index)
{
if(pos == index + 1)
{
temp = alloc_info->next;
alloc_info->next = temp->next;
if(temp->next==NULL)
{
ptr_next = alloc_info;
}
free(temp);
break;
}
}
}
}
/*
* deletes all the elements from the list
*/
void clear()
{
MEM_LEAK * temp = ptr_start;
MEM_LEAK * alloc_info = ptr_start;
while(alloc_info != NULL)
{
alloc_info = alloc_info->next;
free(temp);
temp = alloc_info;
}
ptr_start=NULL;
}
/*
* replacement of malloc
*/
void * xmalloc (unsigned int size, const char * file, unsigned int line)
{
void * ptr = malloc (size);
if (ptr != NULL)
{
add_mem_info(ptr, size, file, line);
}
return ptr;
}
/*
* replacement of calloc
*/
void * xcalloc (unsigned int elements, unsigned int size, const char * file, unsigned int line)
{
unsigned total_size;
void * ptr = calloc(elements , size);
if(ptr != NULL)
{
total_size = elements * size;
add_mem_info (ptr, total_size, file, line);
}
return ptr;
}
/*
* replacement of free
*/
void xfree(void * mem_ref)
{
remove_mem_info(mem_ref);
free(mem_ref);
}
/*
* gets the allocated memory info and adds it to a list
*
*/
void add_mem_info (void * mem_ref, unsigned int size, const char * file, unsigned int line)
{
MEM_INFO mem_alloc_info;
/* fill up the structure with all info */
memset( &mem_alloc_info, 0, sizeof ( mem_alloc_info ) );
mem_alloc_info.address = mem_ref;
mem_alloc_info.size = size;
strncpy(mem_alloc_info.file_name, file, FILE_NAME_LENGTH);
mem_alloc_info.line = line;
/* add the above info to a list */
add(mem_alloc_info);
}
/*
* if the allocated memory info is part of the list, removes it
*
*/
void remove_mem_info (void * mem_ref)
{
unsigned short index;
MEM_LEAK * leak_info = ptr_start;
/* check if allocate memory is in our list */
for(index = 0; leak_info != NULL; ++index, leak_info = leak_info->next)
{
if ( leak_info->mem_info.address == mem_ref )
{
erase ( index );
break;
}
}
}
/*
* writes all info of the unallocated memory into a file
*/
void report_mem_leak(void)
{
unsigned short index;
MEM_LEAK * leak_info;
FILE * fp_write = fopen (OUTPUT_FILE, "w+");
char info[1024];
if(fp_write != NULL)
{
sprintf(info, "%s
", "Memory Leak Summary");
fwrite(info, (strlen(info)) , 1, fp_write);
sprintf(info, "%s
", "-----------------------------------");
fwrite(info, (strlen(info)) , 1, fp_write);
for(leak_info = ptr_start; leak_info != NULL; leak_info = leak_info->next)
{
sprintf(info, "address : %d
", leak_info->mem_info.address);
fwrite(info, (strlen(info)) , 1, fp_write);
sprintf(info, "size : %d bytes
", leak_info->mem_info.size);
fwrite(info, (strlen(info)) , 1, fp_write);
sprintf(info, "file : %s
", leak_info->mem_info.file_name);
fwrite(info, (strlen(info)) , 1, fp_write);
sprintf(info, "line : %d
", leak_info->mem_info.line);
fwrite(info, (strlen(info)) , 1, fp_write);
sprintf(info, "%s
", "-----------------------------------");
fwrite(info, (strlen(info)) , 1, fp_write);
printf("addr=%d,size=%d,file=%s,line=%d
"
,leak_info->mem_info.address
,leak_info->mem_info.size
,leak_info->mem_info.file_name
,leak_info->mem_info.line);
}
fclose(fp_write);
}
else
{
}
/*clear();*/
}
使用时,只需将以上两个文件放入到你的工程目录下,并在主函数里面调用report_mem_leak就OK了。
测试test.c文件
#include "leak_detector_c.h"
int main()
{
char * ptr1 = (char *)malloc(10);
int * ptr2 = (int *)calloc(10, sizeof(int));
float * ptr3 = (float *) calloc(15, sizeof(float));
free(ptr2);
atexit(report_mem_leak);
return 0;
}
编译test.c并运行,可以在/home/leak_info.txt文件中,看到两条内存泄露记录。
总的来说,这样的检测方式还是有一定效果的,但是同时内存信息链表节点也使用了malloc来申请内存,这就造成了一定的额外的内存开销。
我觉得要避免内存泄露,首先是自身要养成良好的编程习惯,然后是使用相关的方法和工具对代码进行检测,最后是要阅读代码了。工具也并不能检测出所有的内存泄露,在这个时候只能靠自己去阅读代码分析代码了,人毕竟比机器灵活嘛!
./autogen.sh./configure --host=powerpc-linux CC=powerpc-linux-gnu-gcc CPP=powerpc-linux-gnu-cpp CXX=powerpc-linux-gnu-g++ --prefix=/mnt/nandmake
make install
./valgrind --tool=memcheck
--vgdb=no ./memleak
注意:
1. --prefix=/mnt/nand 指定的目录要与开发板上放置的目录一致
2. 参数必须 --vgdb=no 否则会如下错误(可能因为我嵌入式linux 没有集成gdb)
root@xxx:/mnt/nand/bin# ./valgrind --tool=memcheck ./memleak
==1300== Memcheck, a memory error detector
==1300== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==1300== Using Valgrind-3.9.0 and LibVEX; rerun with -h for copyright info
==1300== Command: ./memleak
==1300==
==1300== error 2 No such file or directory
==1300== mknod /tmp/vgdb-pipe-from-vgdb-to-1300-by-root-on-/bin/hostname
==1300== valgrind: fatal error: vgdb FIFOs cannot be created.