【内存泄露/C++】基于重载new运算符的内存泄露检测工具
来源:互联网 发布:色系shipin软件下载 编辑:程序博客网 时间:2024/06/05 06:54
···检测内存泄露的方法有很多,这里使用的是重载系统的new运算符和delete运算符。从而达到,在new的时候记录申请内存的大小,和代码所属文件和行号,后两个信息主要是为了调试。在程序结束之前,访问保存的这些信息,判断内存是否泄漏。
···这里使用的存储结构是哈希表,结构如下:
typedef struct MemNode//记录new时保存的信息{ int size; char* file; int line;}MemNode;#define BUCKET_NUM 3typedef struct hash_node{ MemNode* bucket[BUCKET_NUM];//桶结点 hash_node* link;}hash_node;
···我们要检测到内存泄露,肯定要在程序结束的时候判断。这里指的程序结束,不是main函数的最后一句,而是所有的对象析构完的时候。我们知道,对象析构的顺序是先声明的最后释放,我们利用这个特性,可以声明一个全局的对象,在他的析构函数里判断内存泄露:
class VLD{public: VLD() { creatHashtable(ht); //初始化哈希表 hash_table_size = 0; } ~VLD() { check_vld();//检测 }};
···重载new运算符的函数原型为:
void* operator new(size_t sz ,char* filename, int line);
···这里重载的是operator new 另外new操作符还有两种重载方式,
void* operator new(size_t sz ,char* filename, int line){ void* result; hash_table_size++; int totle_size = sz + sizeof(MemNode); MemNode* s = (MemNode*)malloc(totle_size); s->file = filename; s->line = line; s->size = sz; result = s + 1; int index = Hash(result); return result;}
···很容易就写出了重载的new函数,但是,我们并没有把存储信息的结点放入哈希表中,放在哈希表是为了delete的时候快速找到,当然,用链表存储也是可以的,只不过效率略低。
···上图是存储信息的数据结构示意图,和一般的哈希表不一样的是,哈希表data域放的是MemNode结构体指针,每个结点可以放BUCKET_NUM 个MemNode结构体指针,这样,完整的new函数如下:
void* operator new(size_t sz ,char* filename, int line){ void* result; hash_table_size++; int totle_size = sz + sizeof(MemNode); MemNode* s = (MemNode*)malloc(totle_size); s->file = filename; s->line = line; s->size = sz; result = s + 1; int index = Hash(result);//为生成的Memnode寻找插入点 for (int l = 0; l < BUCKET_NUM; l++) if (ht[index].bucket[l] == NULL) { ht[index].bucket[l] = s; return result; } hash_node* prev = ht + index; while (prev->link != NULL) { for (int l = 0; l < BUCKET_NUM; l++) if (prev->link->bucket[l] == NULL) { prev->link->bucket[l] = s; return result; } prev = prev->link; } hash_node* t = (hash_node*)malloc(sizeof(hash_node)); inithashnode(t); t->bucket[0] = s; prev->link = t; return result;}
···代码略长,如果是用链表存储的,代码可以少很多,但是查找的时候就没hash表快了。用哈希表存,主要是考虑到链表查找性能不高。
···重载的delete函数如下:
void operator delete(void* p){ int index = Hash(p); hash_node* s = ht + index; while (s != NULL) { for (int l = 0; l < BUCKET_NUM; l++) if (s->bucket[l] + 1 == p) { free(s->bucket[l]); s->bucket[l] = NULL; hash_table_size--; return; } s = s->link; }}
···如果这时候我们要使用自己的重载函数,要解决的第一点就是,所属文件和行号怎么传递,如果在new的时候这样写:
int *p = new(__FILE__, __LINE__) int(1);
···这未必太繁琐了,怎样很好的解决呢,这里我们参考C标准中_DEBUG中的写法,定义一个宏:
#define new new(__FILE__,__LINE__)
···这样你写的new int(1),就会自动转为new(FILE,LINE) int(1),继而转到我们的函数。这样做的缺陷很大,会在文章的最后面写出。
···我们再给出检查内存泄露的代码:
void check_vld(){ if (hash_table_size == 0) { cout << "No memory leaks detected" << endl; OutputDebugStringA("No memory leaks detected\n"); return; } cout << " WARNING : Memory leak" << endl; OutputDebugStringA(" WARNING : Memory leak\n"); cout << "-----------------------------------------------------------------------------------------" << endl; OutputDebugStringA("---------------------------------------------------------------------------------------- - \n"); int count = 0, allsize = 0; char str[100] = { 0 }; for (int i = 0; i < P; i++) { hash_node* s = ht + i; while (s) { for (int l = 0; l < BUCKET_NUM; l++) { if (s->bucket[l] != NULL) { count++; allsize += s->bucket[l]->size; sprintf_s(str, "FILE:%s\n", s->bucket[l]->file); OutputDebugStringA(str); sprintf_s(str, "line:%d\n", s->bucket[l]->line); OutputDebugStringA(str); sprintf_s(str, "At:%p Size:%d\n", s->bucket[l] + 1, s->bucket[l]->size); OutputDebugStringA(str); OutputDebugStringA("-----------------------------------------------------------------------------------------\n");#ifdef COUT2APP cout << "FILE:" << s->bucket[l]->file << endl; cout << "LINE:" << s->bucket[l]->line << endl; cout << "At:" << s->bucket[l] + 1 << " Size:" << s->bucket[l]->size << endl; cout << "-----------------------------------------------------------------------------------------" << endl; #endif } } s = s->link; } } sprintf_s(str, "Count: %d, Total %d \n", count, allsize); OutputDebugStringA(str);}
···不要觉得他很长,,,其实全是在控制输入的格式。核心就是判断我们的hash_table是否空,不空则遍历里面的所有元素,说白了,和平时写的show函数异曲同工。
····这样可以算完了吗?当然,还差一步。我们来看这个例子:
void main() { int* p = new int[20]; delete p;}
····在调试窗口显示无内存泄露,这个很好理解,由于int是内置类型,缺少析构函数,就算不用delete【】这样的写法也没关系。···
····但是:
void* operator new[](size_t sz, char* filename, int line){ return operator new(sz, filename, line);}void operator delete[](void* p){ operator delete(p);}
加上之后,用delete【】 释放:
···至此,所有功能函数全部写完。
···其实,很明显的一个缺点,就是把new定义为了宏
···这样做的坏处就是,引入了自己的内存检测头文件,就只能使用最简单的new操作符了,连定位 new 都无法编译通过。
最后,附上完整代码:
#include <corecrt_malloc.h>#include <iostream>#include<Windows.h>#include<stdio.h>#ifndef _VLD_H_#define _VLD_H_using namespace std;#define BUCKET_NUM 3#define P 13//1 4 8 12 16 20 24typedef struct MemNode{ int size; char* file; int line;}MemNode;typedef struct hash_node{ MemNode* bucket[BUCKET_NUM]; hash_node* link;}hash_node;typedef hash_node hash_table[P];static hash_table ht;static int hash_table_size;void creatHashtable(hash_table& ht){ for (int i = 0; i < P; i++) { for (int l = 0; l < BUCKET_NUM; l++) ht[i].bucket[l] = NULL; ht[i].link = NULL; }}void inithashnode(hash_node*& t){ t->link = NULL; for (int l = 0; l < BUCKET_NUM; l++) t->bucket[l] = NULL;}int Hash(void* point){ return (int)point % P;}void* operator new(size_t sz ,char* filename, int line){ void* result; hash_table_size++; int totle_size = sz + sizeof(MemNode); MemNode* s = (MemNode*)malloc(totle_size); s->file = filename; s->line = line; s->size = sz; result = s + 1; int index = Hash(result); for (int l = 0; l < BUCKET_NUM; l++) if (ht[index].bucket[l] == NULL) { ht[index].bucket[l] = s; return result; } hash_node* prev = ht + index; while (prev->link != NULL) { for (int l = 0; l < BUCKET_NUM; l++) if (prev->link->bucket[l] == NULL) { prev->link->bucket[l] = s; return result; } prev = prev->link; } hash_node* t = (hash_node*)malloc(sizeof(hash_node)); inithashnode(t); t->bucket[0] = s; prev->link = t; return result;}void operator delete(void* p){ int index = Hash(p); hash_node* s = ht + index; while (s != NULL) { for (int l = 0; l < BUCKET_NUM; l++) if (s->bucket[l] + 1 == p) { free(s->bucket[l]); s->bucket[l] = NULL; hash_table_size--; return; } s = s->link; }}void* operator new[](size_t sz, char* filename, int line){ return operator new(sz, filename, line);}void operator delete[](void* p){ operator delete(p);}void check_vld(){ if (hash_table_size == 0) { cout << "No memory leaks detected" << endl; OutputDebugStringA("No memory leaks detected\n"); return; } cout << " WARNING : Memory leak" << endl; OutputDebugStringA(" WARNING : Memory leak\n"); cout << "-----------------------------------------------------------------------------------------" << endl; OutputDebugStringA("---------------------------------------------------------------------------------------- - \n"); int count = 0, allsize = 0; char str[100] = { 0 }; for (int i = 0; i < P; i++) { hash_node* s = ht + i; while (s) { for (int l = 0; l < BUCKET_NUM; l++) { if (s->bucket[l] != NULL) { count++; allsize += s->bucket[l]->size; sprintf_s(str, "FILE:%s\n", s->bucket[l]->file); OutputDebugStringA(str); sprintf_s(str, "line:%d\n", s->bucket[l]->line); OutputDebugStringA(str); sprintf_s(str, "At:%p Size:%d\n", s->bucket[l] + 1, s->bucket[l]->size); OutputDebugStringA(str); OutputDebugStringA("-----------------------------------------------------------------------------------------\n");#ifdef COUT2APP cout << "FILE:" << s->bucket[l]->file << endl; cout << "LINE:" << s->bucket[l]->line << endl; cout << "At:" << s->bucket[l] + 1 << " Size:" << s->bucket[l]->size << endl; cout << "-----------------------------------------------------------------------------------------" << endl; #endif } } s = s->link; } } sprintf_s(str, "Count: %d, Total %d \n", count, allsize); OutputDebugStringA(str);}class VLD{public: VLD() { creatHashtable(ht); hash_table_size = 0; } ~VLD() { check_vld(); }};#ifdef _DEBUG VLD vldtest; #define new new(__FILE__,__LINE__) #define COUT2APP#endif#endif
- 【内存泄露/C++】基于重载new运算符的内存泄露检测工具
- c++内存泄露之重载new运算符
- Linux C内存泄露检测工具
- Linux C内存泄露检测工具
- Linux C内存泄露检测工具
- Linux C内存泄露检测工具
- linux C内存泄露检测工具
- Linux C内存泄露检测工具
- C\C++ 内存泄露检测工具
- Linux C内存泄露检测工具
- Linux C内存泄露检测工具
- C程序内存泄露检测工具
- 重载new, 查内存泄露
- 内存泄露:重载new delete
- 内存泄露检测工具
- 内存泄露检测工具
- 内存泄露检测工具
- 内存泄露检测工具比较
- android 学习笔记 Pull 解析XML 格式数据。
- Hibernate 缓存机制
- IIS日志-网站运维的好帮手
- iOS pch文件配置
- CSS绘制三角形
- 【内存泄露/C++】基于重载new运算符的内存泄露检测工具
- Lucene学习笔记
- 更改angular $http post 默认json
- 设置单选按钮RadioGroup
- 华为题库—1字符串最后一个单词的长度
- 全屏滑动,截取滑动手势
- JS继承
- 保利威视后台编辑FLASH播放器
- JVM java虚拟机的学习(一)