一个C++的内存池和内存管理的实现(六)
2015-04-18 16:40
615 查看
这章是加入内存泄露的自动查找的。之前已经发布了,CSDN不知道给整哪去了,我自己也没存档。懒得再仔细重写了,随便写一下。
泄露工具有不少,比如微软自己的
首先得在前缀里加几个字段。不过加了之后会比较臃肿,所以用个
然后MemPool加几个相关函数的定义
接下来在内存申请的时候,添加语句用来记录相关文件名和行数。以下所有泄露检查的相关添加和改动都在
接下来在Free函数加一段,用来对每个要释放的指针对象作合法性检测。这个无关泄露,但也提一下吧。
泄露的检查是在MemPool的析构里做的。这样当程序退出时,会打印泄露信息
OK,接下来是泄露检查的实现部分
泄露工具有不少,比如微软自己的
_CrtSetDbgFlag,
_CrtSetBreakAlloc。但是牵涉到静态变量和跨DLL的申请释放时,很多工具会给出不准确的信息。自己做一个吧,调试起来做改动也方便,可以做到有的放矢。
首先得在前缀里加几个字段。不过加了之后会比较臃肿,所以用个
USE_SMART_HEAP宏控制,不需要作检查的时候就关掉。
nRefCnt、
szToken、
bIsArray是以后越界检查才用到的,现在不用管它。
_declspec(align(ALIGNMENT)) struct PrefixNest { ... MemPageHeader* pPage; // page that contains the memory block #if defined USE_SMART_HEAP char szFileName[32]; // 文件名 int nLineNumber; // 行号 int nRefCnt; // 引用数(类似智能指针) char szToken[4]; // signature for being a Prefix bool bIsArray; // 是否数组 #endif };
然后MemPool加几个相关函数的定义
class MemPool { ... private: #if defined USE_SMART_HEAP bool ArePagesAllClear(); // 是否所有页都已回收完毕其下的内存块 void RenderDesc(Prefix*, char*, int); // 输出泄露信息 #endif ... public: #if defined USE_SMART_HEAP // Display a symbolic dump of the heap (i.e. memory pool) by walking the heap and displaying all objects in the heap. void WalkHeap(); // Does the given memory pointer point anywhere into the heap. bool IsPtrOk(void* pMem); // Verify a given memory pointer bool VerifyHeapPointer(void* pMem); #endif ... }
接下来在内存申请的时候,添加语句用来记录相关文件名和行数。以下所有泄露检查的相关添加和改动都在
#if defined USE_SMART_HEAP和
#endif的宏块里。
void* MemPool::Alloc(int nSize, const char* const szFileName, int nLine, bool bIsArray) { if (nSize < MAX_ALLOC_FROM_PAGE) { ... // Check if there is free memory in block pool of the corresponding size if (m_ppBlockPools[i]->d.pNext) // an available memory block in pool { ... pPrefix->d.pPostfix->nActualSize = nSize; // actual size requested #if defined USE_SMART_HEAP if (szFileName) { strcpy_s(pPrefix->d.szFileName, 31, szFileName); pPrefix->d.szFileName[31] = 0; } else pPrefix->d.szFileName[0] = 0; // null string pPrefix->d.nLineNumber = nLine; pPrefix->d.bIsArray = bIsArray; pPrefix->d.nRefCnt = 0; #endif ... } // Allocate from the page ... do { for (MemPageHeader *pPage = m_pFirstPage; pPage != NULL; pPage = pPage->d.pNext) { if (pPage->d.pEnd >= pPage->d.pLast + sizeof(Prefix) + nAllocSize + sizeof(Postfix)) { ... pPrefix->d.pPage = pPage; #if defined USE_SMART_HEAP if (szFileName) { strcpy_s(pPrefix->d.szFileName, 31, szFileName); pPrefix->d.szFileName[31] = 0; } else pPrefix->d.szFileName[0] = 0; // null string pPrefix->d.nLineNumber = nLine; pPrefix->d.bIsArray = bIsArray; *((int*) pPrefix->d.szToken) = PREFIX_SIG; // 加个签名,表明是个前缀 pPrefix->d.nRefCnt = 0; #endif pPage->d.pLast += sizeof(Prefix) + nAllocSize + sizeof(Postfix); ... } } } while (AllocPage()); // now we should allocate one more page ... } ... } void* MemPool::AllocLarge(int nSize, const char* const szFileName, int nLine, bool bIsArray) { ... if (pPrefix) { ... pPrefix->d.pPage = NULL; // indicating it is not allocated from any pages #if defined USE_SMART_HEAP if (szFileName) { strcpy_s(pPrefix->d.szFileName, 31, szFileName); pPrefix->d.szFileName[31] = 0; } else pPrefix->d.szFileName[0] = 0; // null string pPrefix->d.nLineNumber = nLine; pPrefix->d.bIsArray = bIsArray; *((int*) pPrefix->d.szToken) = PREFIX_SIG; pPrefix->d.nRefCnt = 0; #endif ... } ... }
接下来在Free函数加一段,用来对每个要释放的指针对象作合法性检测。这个无关泄露,但也提一下吧。
void MemPool::Free(void< 4000 /span>* pMem) { SYNCHRONIZATION(); #if defined USE_SMART_HEAP if (!VerifyHeapPointer(pMem)) { std::cerr << "Memory block bad" << std::endl; return; } #endif ... }
泄露的检查是在MemPool的析构里做的。这样当程序退出时,会打印泄露信息
MemPool::~MemPool() { // Do some cleanups for (Cleanup *p = m_pCleanups; p != NULL; p = p->pNext) { if (p->pHandler) p->pHandler(p->pParam); } #if defined USE_SMART_HEAP WalkHeap(); #endif ... }
OK,接下来是泄露检查的实现部分
void MemPool::WalkHeap() { if (m_pLarge->d.pNext || !ArePagesAllClear()) { OutputDebugStringA("Detected memory leak:\n"); char buffer[100]; if (m_pLarge->d.pNext) // 大内存泄露 { for (Prefix *pCur = m_pLarge->d.pNext; pCur != NULL; pCur = pCur->d.pNext) { if (!VerifyHeapPointer(&pCur[1])) { OutputDebugStringA("Memory block bad"); break; } RenderDesc(pCur, buffer, 100); // 输出泄露信息 OutputDebugStringA(buffer); } } else // 页里有泄露 { for (MemPageHeader *p = m_pFirstPage; p != NULL; p = p->d.pNext) { Prefix *pPrefix = (Prefix*) ((char*) p + sizeof(MemPageHeader)); while ((char*) pPrefix < p->d.pLast) // 遍历页内所有未能回收的内存块 { if (!IsInBlockPool(pPrefix)) { // 如果不在任何尺寸的池中 RenderDesc(pPrefix, buffer, 100); // 输出泄露信息 OutputDebugStringA(buffer); } pPrefix = (Prefix*) (pPrefix->d.pPostfix + 1); } } } } else OutputDebugStringA("No memory leak:\n"); } bool MemPool::ArePagesAllClear() { for (MemPageHeader *p = m_pFirstPage; p != NULL; p = p->d.pNext) { if (p->d.pLast != (char*) p + sizeof(MemPageHeader)) return false; } return true; } bool MemPool::VerifyHeapPointer(void* pMem) { bool bOk = false; if (IsPtrOk(pMem)) { Prefix *pPrefix = (Prefix*) pMem - 1; if (pPrefix->d.pPostfix->pPrefix == pPrefix) bOk = true; } return (bOk); } bool MemPool::IsPtrOk(void* pMem) { return ((pMem) && (((INT_PTR) pMem & (ALIGNMENT - 1)) == 0)); } void MemPool::RenderDesc(Prefix* pPrefix, char* pBuffer, int nBufferLen) { sprintf_s(pBuffer, nBufferLen, "%08lx ", (DWORD_PTR) pPrefix); if (pPrefix->d.szFileName[0]) sprintf_s(pBuffer + strlen(pBuffer), nBufferLen - strlen(pBuffer), "%s %4ld\n", pPrefix->d.szFileName, pPrefix->d.nLineNumber); // 输出对应文件名和行数 }
相关文章推荐
- 一个C++的内存池和内存管理的实现(九)
- 一个C++的内存池和内存管理的实现(一)
- 一个C++的内存池和内存管理的实现(三)
- 一个C++的内存池和内存管理的实现(二)
- 一个C++的内存池和内存管理的实现(七)
- 一个C++的内存池和内存管理的实现(四)
- C++实现一个简易的内存池分配器
- C++中一个高效的内存池实现
- 让我们一起来实现一个完整的内存管理工具(线程,内存池,萃取)
- C++简单实现一个内存池的分配
- C++模板来实现一个通用的内存池.
- 一个简单实用的内存池实现之二 (C实现)
- KMP算法的一个C++实现
- 分别用C和C++来实现一个链栈
- C++实现一个航空订票程序
- C++ 使用模板实现的一个List
- 一个简单实用的内存池之一(c实现)
- 一个简单的游戏引擎核心状态机的C++实现
- c++和java的内存管理,分别是怎么实现的,怎么防止内存泄露?
- C++: 实现一个简易的银行排号叫号系统