对于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 CrlOut CrlOutW;
#else
#define CrlOut CrlOutA;
#endif
class 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_SECTION m_cs;
};
private:
static std::auto_ptr<Console> m_instance;
BOOL m_bIsConPro;
static Lock m_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 CALLPOS nullptr,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 程序员