一、理解内存泄漏及内存泄漏常见情况
程序中通常包含着静态内存和栈内存。静态内存用来保存局部static对象、类static数据成员以及定义在任何函数之外的变量(全局变量)。栈内存用来保存定义在函数内的非static对象。分配在静态或栈内存中的对象由编译器自动创建和销毁。对于栈对象,仅在定义的程序块运行时才存在,程序退出,栈对象也随即销毁;static对象和全局对象则是在程序结束时销毁。除了静态内存和栈内存,程序还拥有一块内存池,这部分也就是称为堆。在使用堆空间是就需要使用动态内存分配。
内存泄漏:是指程序中己动态分配的堆内存由于某种原因程序未释放或无法释放,造成系统内存的浪费,导致程序运行速度减慢甚至系统崩溃等严重后果。
对于服务器程序及需要长时间运行的程序来说,检测和解决内存泄漏是程序员必备的技能。
1. 内存泄漏出现的情况总结
首先总结一下c++在语法上的错误使用导致的内存泄漏,所以在编写程序时,就尽量避免错误的编写。
(1) 正确的使用new和delete运算符,需要注意的是new和delete要匹配使用,对于初学者这种情况是最常出现的。一般出错的地方像如下的例子,在指针p的值被另一个函数所使用。
char * FunA()
{
char *p = new char;
return p;
}
void FunErrorB()
{
char *b = FunA();
//忘记delete p
}
(2) 释放对象数组时,没有使用delete[]。如例子所示:
Void FunErrorA()
{
Char *p = new char[10];
Delete p;
}
(3) 双指针释放错误,存在指针释放的遗漏。如例子正确的释放一个双指针
Void FunRightA()
{
Char **p = new char*[10];
For(int i=0;i<10;i++)
{
p[i] = new char[10];
}
If(p!=nullptr)
{
For(int i=0;i<10;i++)
{ Delete []p[i];
p[i] = nullptr;
}
Delete []p;
p = nullptr;
}
}
(4)缺少拷贝构造函数。在类里存在成员变量是指针时,在进行赋值=运算和按值传参时,必须重载拷贝构造函数,重新实现其指针拷贝的部分.
(5)没有将基类的析构函数定义为虚函数。当基类指针指向子类对象时,如果基类的析构函数不是virtual,那么子类的析构函数将不会被调用,子类的资源没有正确是释放,因此造成内存泄露。
(6)调用库存在内存泄漏。在使用由个人包装或者未完全测试的库时,要确定此库对本程序不存在性能的影响。
二、使用性能监视器进行内存泄露的确认
对于服务器程序,在不停的运作中,如何去测试是否存在内存泄露,如果你的开发环境是在windows下,那么我们就可以使用windows自带的性能监视器来观察程序的性能。
使用此例子:
Test.c
int main()
{
while (true)
{
char *test = new char[100];
Sleep(100);
}
cin.get();
return 0;
}
循环申请堆空间
(1) 开始运行程序test
(2) 打开性能监视器(Performance Monitor)
(3) 使用性能监视器中的数据收集器,进行数据的收集
(4)创建自定义的数据收集,按下图步骤进行配置。
图1 右击用户定义->新建->数据收集器
图2 填写新建项目名称->手动创建->下一步
图3,选择性能计数器->下一步
图4,点击添加->下一步
图5,选择process(进程选项),processor(处理器),这里的选项由需要监听的属性而定,这里暂且监听的是测试程序该进程的相关属性。
图6,选择该进程需要监听的项目 通常需要监听的是:Handle Count(句柄数)、private Bytes(程序拥有的私有字节)、Virtual bytes(虚拟内存)、working set - private (这个值就是任务管理器里面的内存使用量)
图7,选择多长时间记录一次
图8,下一步
图9图10,选择记录数据用制表符进行分隔,为了能用execl进行统计
图11,开启监听
图12 用execl 打开目标目录下的文件
图13,用折线图进行统计
从图中working set-private上升的曲线可以确定此程序存在内存泄露。
三、定位和解决内存泄露
如果编写的c++程序出现内存泄露了,不要慌忙,你要相信任何错误都是人为造成的,只要是人为的,你一定能找到错误所在,只不过是时间的问题而已。在面对内存泄露,如果程序不是特别长的话,你可以使用人工校验,着重查看new和delete的匹配调用。
如果经过一番折腾还未确定程序的错误所在,首先应该明白程序在不停地循环调用的是那部分程序,然后对这部分程序做特殊的处理,逐行验证,然后确定错误所在。
如果还是找不到,那么我们只能借助工具,进行错误泄露的定位了。
首先,介绍的是VLD(Visual Leak Detector),这是一个免费的、开源的、强大的内存泄露检测系统,可以安装当作VS的一个插件。
http://vld.codeplex.com/
在安装完成后,在工程中指定其include header和library file的路径,然后添加头文件
#include
这样就可以使用了。
如例子:
#include "stdafx.h"
#include
#include
#include
#include
using namespace std;
int main()
{
while (true)
{
char *test = new char[100];
Sleep(1000);
}
cin.get();
return 0;
}
双击1位置的错误列表,即可定位到错误的所在
修改vld.h :ReportFile和ReportTo可以将日志打印到目标文件内。
如果你的程序还存在内存泄露,那你不妨试一下将一些内存申请过程使用智能指针进行实现。