关于 c++ 内存泄漏

来源:互联网 发布:红学会 知乎 编辑:程序博客网 时间:2024/06/05 08:26
        对于windows 32为系统来说,一般一个进程的内存有4Gb,当然这是虚拟内存,windows 用一堆的数据结构来保存 进程,线程,虚拟内存。。。。 在4GB的虚拟内存中,需要映射高2G的内存给操作系统(通过开关设置,可以只配置1G的内存给操作系统),所以对于进程来说,就只有2Gb的虚拟内存,当一个进程被加载的时候,进程的可执行文件需要占用一定的虚拟内存,被加载进来的模块(dll 动态链接库,其实对于windows而言,就是pe文件).也会占用进程虚拟空间。当一个进程被加载的时候,操作系统会分配1MB的内存给进程作为默认堆,当你通过malloc 来分配时,就是从这个默认堆中分配的,因为,默认堆是所有线程共用的,所以分配的时候一定会存在锁,所以分配存在竞争。当然,也不是一定1MB啦,因为我们一般是从main函数开始写,但像我们的编译器会在之前增加很多的代码,默认堆的分配也是编译器做的。你也可以通过编译器设置。我们还可以通过virtualalloc分配一些私有堆,然后写一个分配器,来自己管理内存的分配。每个线程一般编译器会配置1MB的栈,实际先提交4KB。通过页保护来增长栈。但是每个线程只有1MB的栈,并不是无线增长的。ok,浅浅的回忆了一下。有时间,要好好总结一下。
        对于c++而言,内存泄漏是不可避免,但可以采取一些防御手段。
       代码1 可以帮我们干掉一些比较浅显的内存泄漏,避免花时间去找这些bug.
       代码1 的内存分配必须是在类对生存时间里,当该类的对象被释放的时候,该对象里的分配的内存也必须释放的情况。ok,上代码。
       代码1:
commonMacro.h  
#pragma once#define IS_EQUAL(lVal,rVal)((lVal) == (rVal))// equal#define NOT_EQUAL(lVal,rVal)        ((lVal) != (rVal))// not equal#define IS_TRUE(isTrue)IS_EQUAL(true , isTrue)// is true#define NOT_TRUE(notTrue)NOT_EQUAL(true , notTrue)// not true#define IS_FALSE(isFalse)IS_EQUAL(false , isFalse)// is false#define NOT_FALSE(notFalse)NOT_EQUAL(false , notFalse)// not false#define IS_NULLPTR(ptr)IS_EQUAL(nullptr , ptr)// is null#define NOT_NULLPTR(ptr)NOT_EQUAL(nullptr , ptr)// not null
commonMacro.h 是我比较常用的宏,因为,我不想花时间去处理   if( i = 1 ){  xxxx }  这种bug

controlOut.h controlOut.cpp 控制台输出
#pragma once#include "commonMacro.h"#include <wtypes.h>#include <memory>#define CrlOutA(...)do{ Console::check(); printf(__VA_ARGS__); printf("\n");}while(0)#define CrlOutW(...)do{ Console::check(); wprintf(__VA_ARGS__); printf("\n");}while(0)#ifdef _UNICODE#define CrlOutCrlOutW;#else#define CrlOutCrlOutA;#endifclass Console{public:static void  check();private:Console();~Console();static BOOL _isConsoleProgram();static void _initConsoleWindow();int _init();friend class std::auto_ptr<Console>;class Lock{public:Lock(){ InitializeCriticalSection(&m_cs); }~Lock(){ DeleteCriticalSection(&m_cs); }void upLock() { EnterCriticalSection(&m_cs); }void unLock() { LeaveCriticalSection(&m_cs); }private:CRITICAL_SECTIONm_cs;};private:static std::auto_ptr<Console>m_instance;BOOLm_bIsConPro;static Lockm_lock;};
#include "controlOut.h"#include <io.h>#include <stdio.h>#include <fcntl.h>Console::Lock Console::m_lock;std::auto_ptr<Console> Console::m_instance;void Console::check(){if (IS_NULLPTR(m_instance.get())){m_lock.upLock();if (IS_NULLPTR(m_instance.get()))m_instance.reset(new Console);m_lock.unLock();}}Console::Console(){_init();};Console::~Console(){if (IS_FALSE(m_bIsConPro))FreeConsole();}BOOL Console::_isConsoleProgram(){auto pDosH = (PIMAGE_DOS_HEADER)GetModuleHandle(nullptr);auto pNtH = (PIMAGE_NT_HEADERS)((LONG)pDosH + pDosH->e_lfanew);WORD Subsystem = pNtH->OptionalHeader.Subsystem;if (IS_EQUAL(IMAGE_SUBSYSTEM_WINDOWS_CUI, Subsystem) ||IS_EQUAL(IMAGE_SUBSYSTEM_OS2_CUI, Subsystem) ||IS_EQUAL(IMAGE_SUBSYSTEM_POSIX_CUI, Subsystem))return TRUE;return FALSE;}void Console::_initConsoleWindow(){AllocConsole();int nCrt = _open_osfhandle((long)GetStdHandle(STD_OUTPUT_HANDLE), _O_TEXT);FILE* fp = _fdopen(nCrt, "w");*stdout = *fp;setvbuf(stdout, nullptr, _IONBF, 0);}int Console::_init(){int nRet = 0;if (IS_FALSE(_isConsoleProgram())){_initConsoleWindow();m_bIsConPro = FALSE;goto _exit;}m_bIsConPro = TRUE;_exit:SetConsoleTextAttribute(GetStdHandle(STD_OUTPUT_HANDLE), FOREGROUND_GREEN);return nRet;}
控制台输出,是我比较常用的 调试必备代码。适合在GUI程序中弹出控制台输出,用单例,主要是避免在多线程中,多次启动控制台。

接着,就是主题了,采用宏,是为了方便类的使用。模板,慢慢品味吧。看不懂的话,熟悉一下c++11特性。分析一下vs中stl源码。
/**   author: zyb*   \brief  memory  safe malloc  */#pragma once#include <map>#include "commonMacro.h"#include "controlOut.h"#ifdef _DEBUG#include <assert.h>#define __DIGIT2STR(a)#a#define _DIGIT2STR(a)__DIGIT2STR(a)#define _SPACE_STR" "#define _ADDR2INT(a)reinterpret_cast<int>(a)#define _CALLPOS__FUNCTION__\_SPACE_STR\_DIGIT2STR(__LINE__)#define CALLPOS_CALLPOS,sizeof(_CALLPOS)#define NEW_COMMON(pObj,pszPos,iPosLen,m_map)\char* pInfo = new char[iPosLen];\strcpy_s(pInfo,iPosLen,pszPos);\m_map.insert(pair_t(_ADDR2INT(pObj),pInfo));\return pObj;#define DELETE_COMMON(pObj,m_map)\if(IS_NULLPTR(pObj))\return;\memcheck_iterator_t it =\m_map.find(_ADDR2INT(pObj));\assert(NOT_EQUAL(it,m_map.end()));\char* pInfo = it->second;\m_map.erase(it);\delete[] pInfo;#define DECLARE_MEMCHECK_MEMBER(classname)\private:\typedef std::map<int,char*>memcheck_store_t;\typedef memcheck_store_t::iterator memcheck_iterator_t;\typedef std::pair<int,char*> pair_t;\\memcheck_store_t m_##classname##_map;\\template<class _Ty,class... _Types>\_Ty* Safnew(char* pszPos,int iPosLen,_Types... params)\{\_Ty* pObj = new _Ty(params...);\NEW_COMMON(pObj,pszPos,iPosLen,m_##classname##_map);\}\\template<class _Ty>\_Ty* Safnew_a(char* pszPos,int iPosLen,int aLen)\{\_Ty* pObj = new _Ty[aLen];\NEW_COMMON(pObj,pszPos,iPosLen,m_##classname##_map);\}\\template<class _Ty>\void Safdelete(_Ty& pObj)\{\DELETE_COMMON(pObj,m_##classname##_map)\delete pObj;\pObj = nullptr;\\return;\}\template<class _Ty>\void Safedelete_a(_Ty& pObj)\{\DELETE_COMMON(pObj,m_##classname##_map)\delete[] pObj;\pObj = nullptr;\\return;\}#define END_CHECKMEM(classname)\do{\if(IS_TRUE(\m_##classname##_map.empty()))\break;\memcheck_iterator_t it_start =\m_##classname##_map.begin();\memcheck_iterator_t it_end =\m_##classname##_map.end();\for(;it_start != it_end;it_start++)\{\CrlOutA("%s",it_start->second);\m_##classname##_map.erase(it_start);\delete[] it_start->second;\}\assert(0);\}while(0)#else#define CALLPOSnullptr,0// VS's optimize was very powerful#define DECLARE_MEMCHECK_MEMBER(classname)\private:\template<class _Ty,class... _Types>\inline _Ty* Safnew(char* pszPos, int iPosLen,_Types... params)\{\UNREFERENCED_PARAMETER(pszPos);\UNREFERENCED_PARAMETER(iPosLen);\return new _Ty(params...);\}\\template<class _Ty>\inline _Ty* Safnew_a(char* pszPos,int iPosLen,int aLen)\{\UNREFERENCED_PARAMETER(pszPos);\UNREFERENCED_PARAMETER(iPosLen);\return new _Ty[aLen];\}\\template<class _Ty>\inline void Safdelete(_Ty& pObj)\{\if(IS_NULLPTR(pObj))\return;\delete pObj;\pObj = nullptr;\}\\template<class _Ty>\inline void Safdelete_a(_Ty& pObj)\{\if(IS_NULLPTR(pObj))\return;\delete[] pObj;\pObj = nullptr;\}#define END_CHECKMEM(classname)#endif //_DEBUG
原理 包装new delete函数。在分配的时候,记录函数的名字记行号,释放的移除,最后当结束的时候,在检查时候释放完毕。只能在 debug上进行内存追踪。在release上,VS会去优化代码。

演示代码:
#include "MemCheck.h"class pig{DECLARE_MEMCHECK_MEMBER(pig);public:pig(const char* pszName = "name_pig");~pig();void name(){printf("%s\n", m_pName->c_str());}private:std::string*m_pName;};pig::pig(const char* pszName /* = "name_pig" */){m_pName = Safnew<std::string>(CALLPOS,pszName);}pig::~pig(){//Safdelete(m_pName);//未释放END_CHECKMEM(pig);}class dog{DECLARE_MEMCHECK_MEMBER(dog);public:dog(const char* pszName = "name_dog");~dog();void name(){int cchar = m_pName->length();char* ptempname = Safnew_a1<char>(CALLPOS, cchar+1);//局部分配,未释放memcpy(ptempname, m_pName->c_str(), cchar);ptempname[cchar] = 0;printf("%s\n", ptempname);}private:std::string*m_pName;};dog::dog(const char* pszName /* = "name_dog" */){m_pName = Safnew<std::string>(CALLPOS, pszName);}dog::~dog(){Safdelete(m_pName);END_CHECKMEM(dog);//检测内存}class cat{DECLARE_MEMCHECK_MEMBER(cat);public:cat(const char* pname = "name_cat");~cat();char* getCatName(){int cchar = m_pName->length();char* ptempname = new char[cchar + 1];//Safnew_a1<char>(CALLPOS, cchar + 1);//在这里不适合使用Safnew_al  因为分配的内                                                                                            //存被其他类使用memcpy(ptempname, m_pName->c_str(), cchar);ptempname[cchar] = 0;return ptempname;}private:std::string* m_pName;};cat::cat(const char* pname /* = "name_cat" */){m_pName = Safnew<std::string>(CALLPOS, pname);}cat::~cat(){Safdelete(m_pName);END_CHECKMEM(cat);}class Zoo{DECLARE_MEMCHECK_MEMBER(Zoo);public:Zoo(const char* pigname = "name_pig",const char* dogname = "name_dog",const char* catname = "name_cat");~Zoo();void name(){m_pPig->name();m_pDog->name();const char* cat_name = m_pCat->getCatName();printf("%s\n", cat_name);delete[] cat_name;//外部类释放}public:pig *m_pPig;dog *m_pDog;cat *m_pCat;};Zoo::Zoo(const char* pigname /* = "name_pig" */,const char* dogname /* = "name_dog" */,const char* catname /* = "name_cat" */){m_pPig = Safnew<pig>(CALLPOS, pigname);m_pDog = Safnew<dog>(CALLPOS, dogname);m_pCat = Safnew<cat>(CALLPOS, catname);}Zoo::~Zoo(){Safdelete(m_pPig);Safdelete(m_pDog);END_CHECKMEM(Zoo);}void main(){Zoo test; test.name();}

ok ,这就是代码1了 ,不知道为什么,不太想解释太多。明天周末了,晚上去玩一晚英雄联盟。不知道什么时候开始了这个习惯。 i just a 程序员