重载new和delete来检测内存泄漏
重载new和delete来检测内存泄漏
1. 简述
内存泄漏属于资源泄漏的一种,百度百科将内存泄漏分为四种:常发性内存泄漏、偶发性内存泄漏、一次性内存泄漏和隐式内存泄漏。
常发性指:内存泄漏的代码会被多次执行到。偶发性指:内存泄漏的代码只有在特定的条件下才会执行到。一次性指:内存泄漏的代码只会被执行到一次。隐式指:程序在运行中不断的开辟内存,知道程序结束时才释放内存,本质上虽然没有内存泄漏,但是如果这个程序在连续运行很长时间,会耗尽所有内存,导致系统崩溃。
下面首先介绍内存检测的基本原理,然后给出代码样例,最后说明针对四种内存泄漏进行检测的想法。
2. 基本原理
内存泄漏就是new出来的内存没有通过delete合理的释放掉。new和delete这两个函数就是关键点。可以重载new和delete,每次new中开辟一块内存就用链表把这个内存的信息保存下来,每次用delete删除一块内存就从链表中删除这块内存的记录。
3. 代码样例
1 #include<iostream>
2 using namespace std;
3 //---------------------------------------------------------------
4 // 内存记录
5 //---------------------------------------------------------------
6 class MemInfo {
7 private:
8 void* ptr;
9 const char* file;
10 unsigned int line;
11 MemInfo* link;
12 friend class MemStack;
13 };
14 //---------------------------------------------------------------
15 // 内存记录栈
16 //---------------------------------------------------------------
17 class MemStack {
18 private:
19 MemInfo* head;
20 public:
21 MemStack():head(NULL) { }
22 ~MemStack() {
23 MemInfo* tmp;
24 while(head != NULL) {
25 free(head->ptr); // 释放泄漏的内存
26 tmp = head->link;
27 free(head);
28 head = tmp;
29 }
30 }
31 void Insert(void* ptr, const char* file, unsigned int line) {
32 MemInfo* node = (MemInfo*)malloc(sizeof(MemInfo));
33 node->ptr = ptr; node->file = file; node->line=line;
34 node->link = head; head = node;
35 }
36 void Delete(void* ptr) {
37 MemInfo* node = head;
38 MemInfo* pre = NULL;
39 while(node != NULL && node->ptr!=ptr) {
40 pre = node;
41 node = node->link;
42 }
43 if(node == NULL)
44 cout << "删除一个没有开辟的内存" << endl;
45 else {
46 if(pre == NULL) // 删除的是head
47 head = node->link;
48 else
49 pre->link = node->link;
50 free(node);
51 }
52 }
53 void Print() {
54 if(head == NULL) {
55 cout << "内存都释放掉了" << endl;
56 return;
57 }
58 cout << "有内存泄露出现" << endl;
59 MemInfo* node = head;
60 while(node != NULL) {
61 cout << "文件名: " << node->file << " , " << "行数: " << node->line << " , "
62 << "地址: " << node->ptr << endl;
63 node = node->link;
64 }
65 }
66 };
67 //---------------------------------------------------------------
68 // 全局对象 mem_stack记录开辟的内存
69 //---------------------------------------------------------------
70 MemStack mem_stack;
71 //---------------------------------------------------------------
72 // 重载new,new[],delete,delete[]
73 //---------------------------------------------------------------
74 void* operator new(size_t size, const char* file, unsigned int line) {
75 void* ptr = malloc(size);
76 mem_stack.Insert(ptr, file, line);
77 return ptr;
78 }
79 void* operator new[](size_t size, const char* file, unsigned int line) {
80 return operator new(size, file, line); // 不能用new
81 }
82 void operator delete(void* ptr) {
83 free(ptr);
84 mem_stack.Delete(ptr);
85 }
86 void operator delete[](void* ptr) {
87 operator delete(ptr);
88 }
89 //---------------------------------------------------------------
90 // 使用宏将带测试代码中的new和delte替换为重载的new和delete
91 //---------------------------------------------------------------
92 #define new new(__FILE__,__LINE__)
93 //---------------------------------------------------------------
94 // 待测试代码
95 //---------------------------------------------------------------
96 void bad_code() {
97 int *p = new int;
98 char *q = new char[5];
99 delete []q;
100 }
101
102 void good_code() {
103 int *p = new int;
104 char *q = new char[5];
105 delete p;
106 delete []q;
107 }
108 //---------------------------------------------------------------
109 // 测试过程
110 //---------------------------------------------------------------
111 int main() {
112 good_code();
113 bad_code();
114 mem_stack.Print();
115 system("PAUSE");
116 return 0;
117 }
输出结果为:

可见97行开辟的int,没有delete掉,输出结果也显示为97行。
4. 代码说明
4.1 关于new的参数问题。
对于new int,编译器会解释为new(sizeof(int)),对于new int[5],编译器会解释为new(sizeof(int)*5)。因此使用宏定义预编译后,new int就变为new (__FILE__,__LINE__) int,编译器会解释为new(sizeof(int), __FILE__,__LINE__)。
4.2 关于MemStack
MemStack内部也是一个链表结构,注意内部实现不能使用new和delete,只能使用malloc和free来实现链表,因为待测代码中的重载new和delete中调用了MemStack的insert和delete函数,如果insert和delete函数也调用重载后的new和delete的话,会构成死循环的,所以直接使用free和malloc比较好。
MemStack中的析构函数,会释放掉泄漏掉的内存。
5. 使用思考
对于常发性和一次性的内存泄漏代码,直接放入测试就好了。对于偶发性的内存泄漏代码,只要满足特定条件,那么也就转化为常发性或者一次性的内存泄漏了。
对于隐式内存泄漏,由于程序是在很长一段时间之后导致内存耗尽,我们需要长时间观察,每隔一段时间比较一下内存的使用量,如果在一个较长的时间内,内存使用量持续增加,那么可以考虑是内存泄漏。不过调试起来可能会比较麻烦,还是需要重新审视程序设计的。
6. 参考
百度百科_内存泄漏:介绍内存泄漏的基本分类。
http://baike.baidu.com/view/714962.htm
如何检查内存泄漏-重载new和delete:十分生动的说明。
http://www.cppblog.com/dawnbreak/articles/76223.html
一个跨平台的C++内存泄漏检测器:十分专业化的讲解和实现。
http://www.ibm.com/developerworks/cn/linux/l-mleak2/index.html
重载new和delete来检测内存泄漏的更多相关文章
- 如何在linux下检测内存泄漏
之前的文章应用 Valgrind 发现 Linux 程序的内存问题中介绍了利用Linux系统工具valgrind检测内存泄露的简单用法,本文实现了一个检测内存泄露的工具,包括了原理说明以及实现细节. ...
- Vc 检测内存泄漏
启用内存泄漏检测 检测内存泄漏是 C/c + + 调试器和 C 运行时库 (CRT) 的主要工具调试堆函数. 若要启用调试堆的所有函数,在 c + + 程序中,按以下顺序包含以下语句: C++复制 # ...
- 如何在linux下检测内存泄漏(转)
本文转自:http://www.ibm.com/developerworks/cn/linux/l-mleak/ 本文针对 linux 下的 C++ 程序的内存泄漏的检测方法及其实现进行探讨.其中包括 ...
- Qt creator 搭配 valgrind 检测内存泄漏
继上次重载operator new检测内存泄漏失败之后,妥协了.决定不管是否是准确指明哪一行代码出现内存泄漏,只要告诉我是否有泄漏就行了,这样就没有new替换的问题.在开发中,总是一个个小功能的开发. ...
- 面向开发的内存调试神器,如何使用ASAN检测内存泄漏、堆栈溢出等问题
GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 目录 介绍 如何使用 ASAN 检测内存泄漏 检测悬空指针访问 检测堆溢出 C++ 中的new/delete不匹配 检测栈 ...
- Android性能优化之利用LeakCanary检测内存泄漏及解决办法
前言: 最近公司C轮融资成功了,移动团队准备扩大一下,需要招聘Android开发工程师,陆陆续续面试了几位Android应聘者,面试过程中聊到性能优化中如何避免内存泄漏问题时,很少有人全面的回答上来. ...
- 使用Visual Leak Detector检测内存泄漏[转]
1.初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题 ...
- monkey检测内存泄漏
monkey中检查内存泄漏,实际上是对一个操作多次操作后看内存情况,内存泄漏具体的原理可百度,现在我们梳理检测内存泄漏的方法: 测试前你需要安装: 1.MAT分析工具 2.使用工具事实监控内存指标,现 ...
- VC使用CRT调试功能来检测内存泄漏
信息来源:csdn C/C++ 编程语言的最强大功能之一便是其动态分配和释放内存,但是中国有句古话:“最大的长处也可能成为最大的弱点”,那么 C/C++ 应用程序正好印证了这句话.在 C/C+ ...
随机推荐
- Linux 操作之基础命令
1.罗列出文件和文件夹 –ls ls 是帮助我们罗列出当前目录下的所有的文件和文件夹,当然了,还可以加上许多选项,最为重要的是所加的参数可以进行组合,起到让人意想不到的效果,下面就是常用的一些ls的及 ...
- java,http的post和get
使用Java发送GET.POST请求 --节选自<疯狂Java讲义> URL的openConnection()方法将返回一个URLConnection对象,该对象表示应用程序和 UR ...
- Linux环境编程导引
计算机系统硬件组成 总线 贯穿整个系统的一组电子管道称为总线, 分为: 片内总线 系统总线 数据总线DB 地址总线AB 控制总线CB 外部总线 I/O设备 I/O设备是系统与外界联系的通道 键盘鼠标是 ...
- 学习tornado:介绍
前言 在python里面,有许多web framework.对于我来说,因为很长一段时间都在使用tornado,所以有了一些心得体会.虽然现在我的工作重点已经转向go了,但是我觉得还是有必要记录一下t ...
- Dynamics CRM 通过OData查询数据URI中包含中文的情况
filter条件如下"?$filter=new_name eq '采购主管' and new_entityname eq 'new_purchaseenquiry' ",如果用这个 ...
- Xcode中的调试工具栏简介
如下图所示: 从左至右,第一个按钮用来隐藏调试区域. 第二个按钮向你展示断点是否被全局开启或禁用.如果它不是高亮蓝色,则没有断点会被触发. 第三个按钮暂停或继续程序的执行,你一般点击它继续运行到程序的 ...
- SpriteBuilder中节点的%位置移动
在SpriteBuilder中可以将一个节点的位置设为%形式,这意味着在不同的屏幕尺寸中,该节点会定位在相对同一个位置. 比如x和y分别为 50%和50%的位置,在各种屏幕中都会定位到屏幕的中心. 但 ...
- 如何手动实现C语言中的字符串操作
学了字符串操作,很多人也许学了大概知道怎么用,但是太久没用就忘了,恰恰这是找软件工程师或者嵌入式工程师以及C,C++相关的笔试面试必考的题目!接下来我们来看看如何手动实现这些相关的函数. 废话不多说, ...
- 4.windows环境下如何安装memcached教程(转载+自己整理)
Memcached 是一个开源免费高性能的分布式内存对象缓存系统,能够加快网站访问速度和减轻数据库压力,本文向大家介绍下windows环境下如何安装memcached.百度经验:jingyan. ...
- C语言生成32位和64位随机数算法
C语言生成32位和64位随机数算法 /** * randstd.h * * Standard definitions and types, Bob Jenkins * * 2015-01-19: re ...