之所以撰写这篇文章是因为前段时间花费了很大的精力在已经成熟的代码上再去处理memory leak问题。写此的目的是希望我们应该养成良好的编码习惯,尽可能的避免这样的问题,因为当你对着一大片的代码再去处理此类的问题,此时无疑增加了解决的成本和难度。准确的说属于补救措施了。
1. 什么是内存泄漏(memory leak)?
 指由于疏忽或错误造成程序未能释放已经不再使用的内存的情况。内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。 

A memory leak is a particular type of unintentional memory consumption by a computer program where the program fails to release memory when no longer needed. This condition is normally the result of a bug
in a program that prevents it from freeing up memory that it no longer needs.This term has the potential to be confusing, since memory is not physically lost from the computer. Rather, memory is allocated to a program, and that program subsequently loses the
ability to access it due to program logic flaws.

2. 对于C和C++这种没有Garbage Collection 的语言来讲,我们主要关注两种类型的内存泄漏:

堆内存泄漏(Heap leak)。对内存指的是程序运行中根据需要分配通过malloc,realloc new等从堆中分配的一块内存,再是完成后必须通过调用对应的 free或者delete 删掉。如果程序的设计的错误导致这部分内存没有被释放,那么此后这块内存将不会被使用,就会产生Heap Leak.

  系统资源泄露(Resource Leak).主要指程序使用系统分配的资源比如 Bitmap,handle ,SOCKET等没有使用相应的函数释放掉,导致系统资源的浪费,严重可导致系统效能降低,系统运行不稳定。  
3. 如何解决内存泄露?
内存泄露的问题其困难在于1.编译器不能发现这些问题。2.运行时才能捕获到这些错误,这些错误没有明显的症状,时隐时现。3.对于手机等终端开发用户来说,尤为困难。下面从三个方面来解决内存泄露:
第一,良好的编码习惯,尽量在涉及内存的程序段,检测出内存泄露。当程式稳定之后,在来检测内存泄露时,无疑增加了排除的困难和复杂度。
使用了内存分配的函数,要记得要使用其想用的函数释放掉,一旦使用完毕。
Heap memory:
malloc\realloc ------  free
new \new[] ----------  delete \delete[]
GlobalAlloc------------GlobalFree 
要特别注意数组对象的内存泄漏
     MyPointEX *pointArray =new MyPointEX [100];
      其删除形式为:
     delete []pointArray 
Resource Leak :对于系统资源使用之前要仔细看起使用方法,防止错误使用或者忘记释放掉系统资源。
我们看MSDN上一个创建字体的例子:
 RECT rect;
HBRUSH hBrush;
FONT hFont;
hdc = BeginPaint(hWnd, &ps);
 hFont = reateFont(48,0,0,0,FW_DONTCARE,FALSE,TRUE,FALSE,DEFAULT_CHARSET,OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS,CLEARTYPE_QUALITY, VARIABLE_PITCH,TEXT("Impact"));
SelectObject(hdc, hFont); 
SetRect(&rect, 100,100,700,200);
SetTextColor(hdc, RGB(255,0,0));

DrawText(hdc, TEXT("Drawing Text with Impact"), -1,&rect, DT_NOCLIP);

DeleteObject(hFont);  
 EndPaint(hWnd, &ps);
如果使用完成时候忘记释放字体,就造成了资源泄漏。 
   对于基于引用计数的系统对象尤其要注意,因为只有其引用计数为0时,该对象才能正确被删除。而其使用过程中有其生成的新的系统资源,使用完毕后,如果没有及时删除,都会影响其引用计数。
 IDNS *m_pDns//define a DNS object.
   If(NULL == m_pDns)
   IEnv_CreateInstance (m_pEnv,AEECLSID_DNS,(void **) (&m_pDns))

}

 If(m_pDns)
{

Char szbuff[256];

IDNS_AddQuestions(M_pDns,AEEDNSTYPE_A,ADDDNSCLASS_IN,szbuff);

IDNS_Start(m_pDns,this);

const AEEDNSResponse * pDnsResponse = NULL;

IDNS_GetResponse(pMe->m_pDns, &pDnsResponse);

…………………………………………………………

…………………………………………………………..

………………………………………………………..

}

DNS_Release(pMe->m_pDns);//当程序运行到此时,其返回值不是0,是1,其含义是程序已经产生内存泄露了,系统已经有一个由DNS所产生的内核对象没有释放,而当这段代码多次执行之后,内存泄露将不断增加……..

m_pDns=NULL;

}

看起来很不直观,仔细分析就会发现,对象pDnsResponse是从m_pDns产生新的object,所以m_pDns的引用计数会增加,因此在使用完pDnsResponse,应该release 该对象使其引用计数恢复正常。
 
对于资源,也可使用RAII,RAII(Resource acquisition is initialization)资源获取即初始化,它是一项很简单的技术,利用C++对象生命周期的概念来控制程序的资源,例如内存,文件句柄,网络连接以及审计追踪(audit trail)等.RAII的基本技术原理很简单.若希望保持对某个重要资源的跟踪,那么创建一个对象,并将资源的生命周期和对象的生命周期相关联.如此一来,就可以利用C++复杂老练的对象管理设施来管理资源.(有待完善) 
例2: 
Struct ITypeface *pTypeface;
if (pTypeface)
{
IANY_CreateInstance(g_pApplet->m_pIShell,AEECLSID_BTFETypeface,void**)& Typeface);
} 
接下来我们就可以从这个接口上面创建字体,比如
IHFont **pihf=NULL;
   ITypeface_NewFontFromFile(ITypeface,……,&pihf).
   ITypeface_NewFontFrommemory(ITypeface,……..,&pihf)
   ITypeface_NewFontFromClassID(IType,……,&pihf)
 
   但是要切记,这些字体在使用完成后一定要release掉,否则最后 iTypeface的引用计数就是你最后没有删除掉的字体的个数。 
第二,重载  new 和 delete。这也是大家编码过程中常常使用的方法。
下面给出简单的sample来说明。
memchecker.h
structMemIns
{
    void * pMem;
    int m_nSize;
    char m_szFileName[256];
    int m_nLine;
    MemIns * pNext;
};
classMemManager
{
public:
    MemManager();
    ~MemManager();
private:
    MemIns *m_pMemInsHead;
    int m_nTotal;
public:
    static MemManager* GetInstance();
    void Append(MemIns *pMemIns);
    void Remove(void *ptr);
    void Dump(); 
 
};
void *operatornew(size_tsize,constchar*szFile, int nLine);
void operatordelete(void*ptr,constchar*szFile, int nLine);
 void operatordelete(void*ptr);
void*operatornew[] (size_tsize,constchar*szFile,int nLine);
void operatordelete[](void*ptr,constchar*szFile, int nLine);
void operatordelete[](void *ptr);
 
memechecker.cpp
#include"Memchecher.h"
#include<stdio.h>
#include<malloc.h>
#include<string.h>
 
MemManager::MemManager()
{
    m_pMemInsHead=NULL;
    m_nTotal=NULL;
}
MemManager::~MemManager()
{
 
}
voidMemManager::Append(MemIns *pMemIns)
{
    pMemIns->pNext=m_pMemInsHead;
    m_pMemInsHead = pMemIns;
    m_nTotal+= m_pMemInsHead->m_nSize;
 
}
voidMemManager::Remove(void *ptr)
{
    MemIns * pCur = m_pMemInsHead;
    MemIns * pPrev = NULL;
    while(pCur)
    {
        if(pCur->pMem ==ptr)
        {
           if(pPrev)
            {
               pPrev->pNext =pCur->pNext;
            }
           else
            {
               m_pMemInsHead =pCur->pNext;
            }
           m_nTotal-=pCur->m_nSize;
           free(pCur);
           break;
        }
        pPrev = pCur;
        pCur = pCur->pNext;
    }
 
}
voidMemManager::Dump()
{
    MemIns * pp = m_pMemInsHead;
    while(pp)
    {
        printf( "File is %s\n", pp->m_szFileName );
        printf( "Size is %d\n", pp->m_nSize );
        printf( "Line is %d\n", pp->m_nLine );
        pp = pp->pNext;
    }
 
}
 
voidPutEntry(void *ptr,intsize,constchar*szFile, int nLine)
{
    MemIns * p = (MemIns *)(malloc(sizeof(MemIns)));
    if(p)
    {
        strcpy(p->m_szFileName,szFile);
        p->m_nLine = nLine;
        p->pMem = ptr;
        p->m_nSize = size;
        MemManager::GetInstance()->Append(p);
    }
}
voidRemoveEntry(void *ptr)
{
    MemManager::GetInstance()->Remove(ptr);
}
 
 
void *operatornew(size_tsize,constchar*szFile, int nLine)
{
    void * ptr = malloc(size);
    PutEntry(ptr,size,szFile,nLine);
    return ptr;
}
voidoperatordelete(void *ptr)
{
    RemoveEntry(ptr);
    free(ptr);
}
void operatordelete(void*ptr,constchar * file, intline)
{
    RemoveEntry(ptr);
    free(ptr);
}
 
void*operatornew[] (size_tsize,constchar* szFile,intnLine)
{
    void * ptr = malloc(size);
    PutEntry(ptr,size,szFile,nLine);
    return ptr;
}
 
void operatordelete[](void *ptr)
{
    RemoveEntry(ptr);
    free(ptr);
}
 
void operatordelete[](void*ptr,constchar*szFile,intnLine)

{

RemoveEntry(ptr);

    free(ptr);
}
#definenewnew(__FILE__,__LINE__)
MemManagerm_memTracer;
 
MemManager*MemManager::GetInstance()
{
    return &m_memTracer;
void main()

{

    int *plen =newint ;
    *plen=10;
    delete plen;
    char *pstr=newchar[35];
    strcpy(pstr,"hello memory leak");
    m_memTracer.Dump();
    return ;
}
 其主要思路是将分配的内存以链表的形式自行管理,使用完毕之后从链表中删除,程序结束时可检查改链表,其中记录了内存泄露的文件,所在文件的行数以及泄露的大小哦。
第三,Boost 中的smart pointer(待完善,结合大家的建议)
第四,一些常见的工具插件,详见我的Blog中相关文章。
4. 由内存泄露引出内存溢出话题:
所谓内存溢出就是你要求分配的内存超出了系统能给你的,系统不能满足需求,于是会产生内存溢出的问题。
常见的溢出主要有:
内存分配未成功,却使用了它。
常用解决办法是,在使用内存之前检查指针是否为NULL。如果指针p 是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。如果是用malloc 或new 来申请内存,应该用if(p==NULL)或if(p!=NULL)进行防错处理。
内存分配虽然成功,但是尚未初始化就引用它。
内存分配成功并且已经初始化,但操作越过了内存的边界。
例如在使用数组时经常发生下标“多1”或者“少1”的操作。特别是在for 循环语句中,循环次数很容易搞错,导致数组操作越界。
使用free 或delete 释放了内存后,没有将指针设置为NULL。导致产生“野指针”。
程序中的对象调用关系过于复杂,实在难以搞清楚某个对象究竟是否已经释放了内存,此时应该重新设计数据结构,从根本上解决对象管理的混乱局面。(这点可是深有感受,呵呵)
不要忘记为数组和动态内存赋初值。防止将未被初始化的内存作为右值使用。

【VS开发】内存泄漏相关问题的更多相关文章

  1. 【转】android 内存泄漏相关收藏博客。

    关于android内存泄漏的研究   博客建了几个月,都没有去写,一是因为当时换工作,然后又是新入职(你懂的,好好表现),比较忙:二是也因为自己没有写博客的习惯了.现在还算是比较稳定了,加上这个迭代基 ...

  2. Windows平台上C++开发内存泄漏检查方法

    充分的利用调试工具可以非常方便地避免内存泄漏问题. 这里介绍两种方法,互为补充,第一种是VC编译器提供的方法,第二种是专用的内存泄漏检查工具Memmory Validator.这两种方法的基本原理是一 ...

  3. Java内存泄漏相关

    之前学习了javaGC的原理机制,有了一定的了解,现在做一个整理总结,便于理解记忆,包括三个问题: 1. java GC是什么时候做的? 2. java GC作用的东西是什么? 3. java GC具 ...

  4. Android 内存泄漏分析与解决方法

    在分析Android内存泄漏之前,先了解一下JAVA的一些知识 1. JAVA中的对象的创建 使用new指令生成对象时,堆内存将会为此开辟一份空间存放该对象 垃圾回收器回收非存活的对象,并释放对应的内 ...

  5. 在 Linux 平台中调试 C/C++ 内存泄漏方法(转)

    由于 C 和 C++ 程序中完全由程序员自主申请和释放内存,稍不注意,就会在系统中导入内存错误.同时,内存错误往往非常严重,一般会带来诸如系统崩溃,内存耗尽这样严重的后果.本文将从静态分析和动态检测两 ...

  6. Android内存泄漏检測与MAT使用

    公司相关项目须要进行内存优化.所以整理了一些分析内存泄漏的知识以及工作分析过程. 本文中不会刻意的编写一个内存泄漏的程序,然后利用工具去分析它.而是通过介绍相关概念,来分析怎样寻找内存泄漏.并附上自己 ...

  7. MAT工具定位分析Java堆内存泄漏问题方法

    一.MAT概述与安装 MAT,全称Memory Analysis Tools,是一款分析Java堆内存的工具,可以快速定位到堆内泄漏问题.该工具提供了两种使用方式,一种是插件版,可以安装到Eclips ...

  8. 线程上下文类加载器ContextClassLoader内存泄漏隐患

    前提 今天(2020-01-18)在编写Netty相关代码的时候,从Netty源码中的ThreadDeathWatcher和GlobalEventExecutor追溯到两个和线程上下文类加载器Cont ...

  9. Android开发之漫漫长途 番外篇——内存泄漏分析与解决

    该文章是一个系列文章,是本人在Android开发的漫漫长途上的一点感想和记录,我会尽量按照先易后难的顺序进行编写该系列.该系列引用了<Android开发艺术探索>以及<深入理解And ...

随机推荐

  1. Python之multiprocessing模块的使用

    作用:Python多进程处理模块,解决threading模块不能使用多个CPU内核,避免Python GIL(全局解释器)带来的计算瓶颈. 1.开启多进程的简单示例,处理函数无带参数 #!/usr/b ...

  2. 第十一天 unittest参数化模块

    unittest的工作原理:一下例子简单的描述了整个过程 def calc(a,b): return a//b import unittest,HTMLTestRunner import Beauti ...

  3. 35. ClustrixDB 减少device1大小

    ClustrixDB中的device1文件用于所有数据库数据.撤消日志.临时表.binlog和ClustrixDB系统对象.ClustrixDB确保device1文件在集群的所有节点上大小相同.一旦得 ...

  4. RAID技术超详细讲解

    RAID 技术是一种多磁盘技术,面对数据的各方面有着两面性的影响,整体来说优点大于缺点的,下面我将详细介绍一下 RAID ,简称磁盘阵列技术. 一.RAID 概述 1988 年美国加州大学伯克利分校的 ...

  5. 【Python之路】特别篇--生成器(constructor)、迭代器(iterator)、可迭代对象(iterable)

    生成器(constructor) 生成器函数在Python中与迭代器协议的概念联系在一起.包含yield语句的函数会被特地编译成生成器 !!! 当函数被调用时,他们返回一个生成器对象,这个对象支持迭代 ...

  6. html2canvas 使用指南

    html2canvas(document.body).then(function(canvas) { document.body.appendChild(canvas); }); 属性参数: http ...

  7. CDQ解决一些三维偏序的问题

    本来几天前就该记录的东西,硬生生被我拖到了现在,太懒了... 在cdq学习时,二维偏序已经解决了,无非就是先sort使第一维有序,然后再用cdq或者数据结构处理第二维.而三维偏序的时候呢,大佬的做法好 ...

  8. 小程序 swiper 轮播图滚动图片 + 视频

    直奔代码主题wxml: <view class="test_box"> <swiper indicator-dots="{{indicatorDots} ...

  9. 性能指标:TPS 并发数 响应时间 QPS

    响应时间(RT)reponse time指系统对请求作出响应的时间.一般关注平均响应时间和最大响应时间.对于单机没有并发操作的应用系统而言,普遍认为响应时间是一个合理且准确的性能指标,但响应时间并不能 ...

  10. Xshell安装教程及Xshell安装程序集组件时出错的解决方法

    部分小伙伴在安装Xshell的时候可能会遇到这个问题:“Xshell5安装程序集组件{0D7E67F6-1A6A-3A26-AF95-B8E83DDCCC3F}时出错.HRESULT0x80070BC ...