arongnet 阅读(1942) 评论(172)
大家经常听到一个名词叫内存泄漏。到底怎样才会遇到内存泄漏,内存泄漏到底该怎么定位,大家却都很糊涂。实际上我对这个问题也很头疼,下面就是我关于这个问题的一些小看法:

1. 什么叫内存泄漏?
内存泄漏是指你分配了内存,使用完毕后没有正确释放它。这样这个内存就不能再被使用。

例如:
void test()
{
    char * p = new char[MAX_PATH];
    GetModuleFileName(NULL,p, MAX_PATH);
    strcat(p+strlen(p) - 3, "txt");
    CStdioFile file(p);
    file.WriteString("text");
    file.Close();
}

注意:上面的指针p函数退出后就无法再释放,new char[MAX_PATH]分配的内存就泄漏了。

2. 何时我的程序泄漏了?
从外部看,我们很难100%准确的方法去判断是否有内存泄漏。实际上,一块内存是否被泄漏,从外部看只有当进程结束才能知道。因为你完全可以从开始分配一块内存,直到结束才去释放它。

基本上,下面行为不能说明内存泄漏:
a) 任务管理器上显示内存使用增加了1MB
b) 内存使用很多(我见过最大内存使用超过1.5GB的程序)

下面行为可能意味着有内存泄漏:
a) 一个进程的内存使用量按照一个固定的速度稳定的增长(例如每小时增加20MB)

如果可以在VC IDE中运行程序,当程序退出时,系统会报一堆泄漏错误。

3. 如何定位
从编译器角度讲,在分配内存后,任何时候释放都是合适的,因此系统不会知道你何时适合去释放内存。在进程运行过程中,你只能估计是否有内存泄漏,而不能确定一定有内存泄漏。一般来说,我们可以用以下一些办法定位:

a) 使用性能监视器跟踪进程的内存使用情况,如果它在不断增长,且增长速度趋于稳定,一般说明在某个循环性的操作中有内存泄漏
b) 在编译器中按照Debug模式运行程序,运行一定时间后(例如2天),使用正常的方式停止进程
正常情况下,你不会收到任何和内存有关的异常。如果你程序有内存泄漏,你会收到如下错误信息:

Detected memory leaks!
Dumping objects ->
D:\projects\EnumWnd\EnumWndDlg.cpp(185) : {78} normal block at 0x00421330, 100 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
Object dump complete.

注意,这里EnumWndDlg.cpp(185)应该是分配这块内存的地方。这是因为没有所谓的内存泄漏点,只有在某点分配的内存被泄漏了。

4. 如何分析内存泄漏
内存泄漏分析一般是通过分析内存生命周期来进行的。你必须对你的内存的可能使用情况有所了解。在此基础上,你应该了解在那个地方内存应该被释放。如果有内存泄漏,说明程序没有走到那个地方,或者释放代码没有正确执行。因此你可能需要跟踪你代码的执行情况,通过分析为什么代码没有按照预期走到内存释放处,了解为什么内存泄漏了。

常见的原因:

a) 忘记释放
b) 释放的方法错误
例如:把指针加入诸如CArray之类的集合对象中,释放时应该首先通过delete操作删除指针指向的对象,然后才能去调用诸如RemoveAll函数去释放集合中的指针。单纯析构诸如CArray之类的对象,不会自动释放它所包含的指针所指向的对象。

c) 析构函数错误:如果你对象中包含指针,那么你析构函数需要正确的释放这些指针。如果你析构函数不做这些,就会有内存泄漏
d) 线程问题:如果线程被你通过TerminateThread中止,那么它所分配的对象一般不会有机会被释放。这是那些喜欢在线程外中止线程的人经常遇到的问题。


评论列表
馨荣家园
re: 内存泄漏问题分析
顺便说一句:现有的工具很多都是基于猜测的基础上进行内存泄漏检测的,很有帮助但是不能全信。内存泄漏分析主要还是得基于源码分析
周星星
如何减少内存泄漏的bug

1。尽量使用C++标准模板容器,它们是可信任的。
比如字符串
   a. 对于不用于传给API改写的字符串,尽量使用string
   b. 对于需要传给API改写的字符串,尽量使用vector<char>,使用前先调用 vector.resize();

2。对于单个堆变量,使用 auto_ptr

3。对于一组堆变量,使用 vector

4。(这一点仅对那些一窍不通且又顽劣不化者说)没事少new点。
5。(这一点仅对那些一窍不通且又顽劣不化者说)在标准容器中别放指针,除非你能肯定它不需要你释放。

对于楼主的代码,我会这样写:
#include <iostream>
#include <vector>
#include <fstream>
#include <windows.h>

void test()
{
    using namespace std;

    vector<char> str( MAX_PATH ); // 当然 char str[MAX_PATH] 是最好的选择
    char* p = &str[0]; // 在str改变容量大小前,p都是有效的。
    GetModuleFileNameA( NULL, p, MAX_PATH ); // 这一句省略正确性判断
    strcpy( p+strlen(p)-3, "txt"); // 不知道你用strcat是干什么,我把它改为strcpy了
    ofstream file( p );
    file << "test";
    file.close();
}

Diviner
re: 内存泄漏问题分析
还是用boundcheck吧。
Diviner
re: 内存泄漏问题分析
这此工具应该不是纯粹猜测,他们中可能只是重载了new,delete,还有其他的内存分配函数。
代码一多,你想基于原代码分析,谈何容易啊。
bc连你createhandle泄漏都捕获得到。
qzgs
re: 内存泄漏问题分析
<a href="http://nbvisa.com/"> 宁波签证公司</a>自2002年成立以来,一直从事美国、英国、加拿大、澳大利亚、新西兰、法国、德国、西班牙、意大利等国家的<a href="http://nbvisa.com/">出国签证</a>事务。从3000多个个人申请案例及124个团体签证案例当中,
<a href="http://nbvisa.com/">宁波意神签证公司</a>的签证律师不断的丰富经验,吸取教训,现已成为唯一几个专业的签证公司之一
美孚、壳牌、长城、埃索、力士、UP、润滑油N-甲基吡咯烷酮(nmp)
美孚、壳牌、长城、埃索、力士、UP、润滑油N-甲基吡咯烷酮(nmp)

发表评论
切换编辑模式