相对于在栈空间分配内存,堆中分配内存其实是非常缓慢的。
另外,由于堆中分配的内存,需要开发者编码回收,当系统非常庞大时,容易出现分配的内容没有回收导致内存泄露的现象。
因此,许多Bible建议开发者尽量使用栈空间,少用甚至不用malloc和free、new和delete;
虽然栈的空间较小,但这样的建议随着计算机的位数从32位升级到64位,越来越成为真理。
但我还是想说,这是有限制的:那就是这条真理适用于多线程程序,但在多协程程序中,由于协程栈空间的限制,极容易撑爆协程栈。
为了提高堆分配内存的速度,内存池出现了。
下面提供固定大小的内存池和可变大小的内存池实现。经过测试,它的性能远高于Boost的内存池哦!
基础头文件base.h
#ifndef __PP_BASE_H__ #define __PP_BASE_H__ #if (defined(WIN32) || defined(WIN64)) #ifndef WINVER // Specifies that the minimum required platform is Windows Vista. #define WINVER 0x0600 // Change this to the appropriate value to target other versions of Windows. #endif #ifndef _WIN32_WINNT // Specifies that the minimum required platform is Windows Vista. #define _WIN32_WINNT 0x0600 // Change this to the appropriate value to target other versions of Windows. #endif #ifndef _WIN32_WINDOWS // Specifies that the minimum required platform is Windows 98. #define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later. #endif #ifndef _WIN32_IE // Specifies that the minimum required platform is Internet Explorer 7.0. #define _WIN32_IE 0x0700 // Change this to the appropriate value to target other versions of IE. #endif ////////////////////////////////////////////////////////////////////////////////////////// #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include <winsock2.h> #include <windows.h> #ifndef PPAPI #define PPAPI __stdcall #endif #else #ifndef PPAPI #define PPAPI #endif #endif #endif
#ifndef __PPMEMORYPOOL_H__ #define __PPMEMORYPOOL_H__ #include "ppbase.h" /** * @brief 可变内存块大小内存池, * 内存池申请的内存不会自动释放,只在内存池销毁时释放。 * 注意:此类“非”线程安全 * 使用示例: * CVarMemoryPool pool; * pool.Create(); * 某一线程: * char * p1 = pool.Malloc(512);//记得加锁 * char * p2 = pool.Malloc(31);//记得加锁 * char * p3 = pool.Malloc(128);//记得加锁 * * 另外一个线程: * pool.Free(p3);//记得加锁 * pool.Free(p2);//记得加锁 * pool.Free(p1);//记得加锁 */ class CVarMemoryPool { struct MemoryPage { MemoryPage* Next; // next memory page }; public: CVarMemoryPool(); ~CVarMemoryPool(); /** * @brief * 创建可变内存池 * @param dwPageSize : 内部分配的内存页大小,内存不够时,内存池会申请一块新的内存页 * @return 创建成功返回TRUE,否则返回FALSE **/ bool PPAPI Create(unsigned int dwPageSize = 0x80000); /** * @brief * 分配Len长度的Buffer * @param dwLen : 获得的内存块大小长度 * @return 返回的内存,如果返回为NULL,则代表分配失败 **/ void* PPAPI Malloc(unsigned int dwLen); /** * @brief * 回收内存 * @param p : 指向需要回收的内存 * @return void **/ void PPAPI Free(void* p); /** * @brief * 清空内存池,使所有的内存都可以使用,此方法不会将内存返回给操作系统 * @return void **/ void PPAPI Clear(); /** * @brief * 获取当前内存使用量 * @return 当前内存使用量 **/ int PPAPI GetMemUsed(); private: void* GetPoolMemory(unsigned int dwLen); void FreePoolMemory(void* pMemBlock, unsigned char dwType); bool AddFreeMemory(int dwIndex); bool SetMemoryPage(); inline char* GetPageBufGegin(MemoryPage *pPage) { return (char*)(pPage + 1); } inline char* GetPageBufEnd(MemoryPage *pPage) { return (char*)(pPage + 1) + m_nPageSize; } private: static const unsigned int ALIGNMENT = 8; static const unsigned int ALLOC_COUNT = 16; static const unsigned int MIN_PAGESIZE = 0x40000; // min pPage size static const unsigned int MAX_UNIT_SIZE = 128; static const unsigned int UNIT_TYPE_COUNT = 16; char* m_pFreeHead[UNIT_TYPE_COUNT]; int m_nFreeCount[UNIT_TYPE_COUNT]; MemoryPage* m_pHeadPage; MemoryPage* m_pWorkPage; char* m_pPageBuf; unsigned int m_nPageSize; }; /** *@brief 固定内存块大小内存池,用于分配固定大小的内存块 * 内存池申请的内存不会自动释放,只在内存池销毁时释放 * 注意:此类“非”线程安全 * 使用示例: * CFixMemoryPool pool; * pool.Create(128); * 某一线程: * char * p1 = pool.Malloc();//记得加锁 * char * p2 = pool.Malloc();//记得加锁 * char * p3 = pool.Malloc();//记得加锁 * * 另外一个线程: * pool.Free(p3);//记得加锁 * pool.Free(p2);//记得加锁 * pool.Free(p1);//记得加锁 */ class CFixMemoryPool { struct MemoryPage { MemoryPage* Next; // next memory page int nFreeHead; // the first free unit in page int nFreecount; // free unit in page }; public: CFixMemoryPool(); ~CFixMemoryPool(); /** * @brief * 初始化内存池 * @param dwUnitSize : 每一个分配的内存块大小 * @param dwPageSize : 内部分配的内存页大小,内存不够时,内存池会申请一块新的内存页 * @return 创建成功返回true,失败返回false **/ bool PPAPI Create(unsigned int dwUnitSize, unsigned int dwPageSize = 0x40000); /** * @brief * 得到一块新的内存 * @return void* **/ void* PPAPI Malloc(); /** * @brief * 归还一块分配的内存 * @param p : 内存的地址 * @return void **/ void PPAPI Free(void* p); /** * @brief * 清空内存池,使所有的内存都可以使用,此方法不会将内存返回给操作系统 * @return void **/ void PPAPI Clear(); /** * @brief * 获取当前内存使用量 * @return 当前内存使用量 **/ int PPAPI GetMemUsed(); private: bool AddMemoryPage(); void InitPage(MemoryPage *pPage); inline char* GetPageBuf(MemoryPage *pPage) { return (char*)(pPage + 1); } private: static const int ALIGNMENT = 4; static const unsigned int MIN_PAGESIZE = 0x40000; // min page size MemoryPage* m_pHeadPage; // first page unsigned int m_nUnitSize; // unit memory size unsigned int m_nPageSize; // total memory in page }; #endif
#include "memorypool.h" #include <assert.h> #include <stdio.h> #include <stdlib.h> #include <malloc.h> CVarMemoryPool::CVarMemoryPool() :m_pHeadPage(NULL), m_pWorkPage(NULL), m_pPageBuf(NULL) { for (unsigned int i = 0; i < UNIT_TYPE_COUNT; ++ i) { m_pFreeHead[i] = NULL; m_nFreeCount[i] = 0; } } CVarMemoryPool::~CVarMemoryPool() { MemoryPage* pMemoryPage = m_pHeadPage; while (m_pHeadPage != NULL) { pMemoryPage = m_pHeadPage->Next; free(m_pHeadPage); m_pHeadPage = pMemoryPage; } } void* CVarMemoryPool::Malloc(unsigned int Len) { assert(Len > 0); Len ++; if (Len > MAX_UNIT_SIZE) { // allocate memory from system if requery Len is too large void* buf = malloc(Len); if (buf == NULL) { return NULL; } //if content of 1 byte before memory means allocate form system *(char*)buf = 0; return (char*)buf + 1; } else { return GetPoolMemory(Len); } } void CVarMemoryPool::Free(void* p) { assert(p != NULL); char* temp = (char*)p - 1; unsigned char type = *temp; if (type == 0) //if content of 1 byte before memory means allocate form system { free(temp); } else { FreePoolMemory(temp, type); } } void* CVarMemoryPool::GetPoolMemory(unsigned int Len) { Len = (Len + (ALIGNMENT-1)) & ~(ALIGNMENT-1); int idx = (Len - 1) / ALIGNMENT; //if free memory unit is not enough, first get some free units if (m_nFreeCount[idx] == 0 && !AddFreeMemory(idx)) { return NULL; } -- m_nFreeCount[idx]; char* buf = m_pFreeHead[idx]; m_pFreeHead[idx] = (char*)(*((INT64*)m_pFreeHead[idx])); *buf = idx + 1; return buf + 1; } void CVarMemoryPool::FreePoolMemory(void* memblock, unsigned char type) { int idx = type - 1; *(INT64*)memblock = (INT64)m_pFreeHead[idx]; m_pFreeHead[idx] = (char*)memblock; ++ m_nFreeCount[idx]; } bool CVarMemoryPool::AddFreeMemory(int idx) { const int UNIT_SIZE = (idx + 1) * ALIGNMENT; if ((m_pPageBuf + UNIT_SIZE ) > GetPageBufEnd(m_pWorkPage) && !SetMemoryPage()) { return false; } char* page_end = GetPageBufEnd(m_pWorkPage); for (unsigned int i = 0; i < ALLOC_COUNT; ++ i) { *(INT64*)m_pPageBuf = (INT64)m_pFreeHead[idx]; m_pFreeHead[idx] = m_pPageBuf; m_pPageBuf += UNIT_SIZE; ++ m_nFreeCount[idx]; if (m_pPageBuf + UNIT_SIZE > page_end) break; } return true; } bool CVarMemoryPool::SetMemoryPage() { if(m_pWorkPage->Next != NULL) { m_pWorkPage = m_pWorkPage->Next; } else { void* buf = malloc(sizeof(MemoryPage) + m_nPageSize); if (buf == NULL) { return false; } else { MemoryPage* pMemoryPage = (MemoryPage*)(buf); pMemoryPage->Next = NULL; m_pWorkPage->Next = pMemoryPage; m_pWorkPage = pMemoryPage; } } m_pPageBuf = GetPageBufGegin(m_pWorkPage); return true; } int CVarMemoryPool::GetMemUsed() { int used = 0; const int PAGE_SIZE = sizeof(MemoryPage) + m_nPageSize; MemoryPage* pMemoryPage = m_pHeadPage; while (pMemoryPage != NULL) { pMemoryPage = pMemoryPage->Next; used += PAGE_SIZE; } return used; } void CVarMemoryPool::Clear() { m_pWorkPage = m_pHeadPage; m_pPageBuf = GetPageBufGegin(m_pWorkPage); } bool CVarMemoryPool::Create( unsigned int PageSize /*= 0x80000*/ ) { PageSize = (PageSize + (ALIGNMENT-1)) & ~(ALIGNMENT-1); if (PageSize < MIN_PAGESIZE) { m_nPageSize = MIN_PAGESIZE; } else { m_nPageSize = PageSize; } void* buf = malloc(sizeof(MemoryPage) + m_nPageSize); if (buf == NULL) { return false; } else { MemoryPage* pMemoryPage = (MemoryPage*)(buf); pMemoryPage->Next = NULL; m_pWorkPage = pMemoryPage; m_pPageBuf = GetPageBufGegin(m_pWorkPage); m_pHeadPage = m_pWorkPage; } return true; } ////////////////////////////////////////////////////////////////////////////////////////// CFixMemoryPool::CFixMemoryPool() :m_pHeadPage(NULL), m_nUnitSize(0), m_nPageSize(0) { } CFixMemoryPool::~CFixMemoryPool() { MemoryPage* pMemoryPage = m_pHeadPage; while (m_pHeadPage != NULL) { pMemoryPage = m_pHeadPage->Next; free(m_pHeadPage); m_pHeadPage = pMemoryPage; } } void* CFixMemoryPool::Malloc() { MemoryPage* pMemoryPage = m_pHeadPage; while (pMemoryPage != NULL && pMemoryPage->nFreecount == 0) { pMemoryPage = pMemoryPage->Next; } // add new page if space is not enough if (pMemoryPage == NULL) { if(!AddMemoryPage()) { return NULL; } pMemoryPage = m_pHeadPage; } // get unused memory -- pMemoryPage->nFreecount; char* buf = GetPageBuf(pMemoryPage) + pMemoryPage->nFreeHead * m_nUnitSize; pMemoryPage->nFreeHead = *(int*)(buf); return buf; } void CFixMemoryPool::Free(void* p) { // don't check null point for fast MemoryPage* pMemoryPage = m_pHeadPage; char* buf = GetPageBuf(m_pHeadPage); // find point in which page while((p < buf || p > buf + m_nPageSize) && pMemoryPage != NULL) { pMemoryPage = pMemoryPage->Next; buf = GetPageBuf(pMemoryPage); } // do not in any page if (pMemoryPage == NULL) { return; } *(int*)p = pMemoryPage->nFreeHead; pMemoryPage->nFreeHead = ((char*)p - buf) / m_nUnitSize; ++ pMemoryPage->nFreecount; return; } bool CFixMemoryPool::AddMemoryPage() { void* buf = malloc(sizeof(MemoryPage) + m_nPageSize); if (buf == NULL) { return false; } MemoryPage* pMemoryPage = (MemoryPage*)(buf); InitPage(pMemoryPage); if (m_pHeadPage == NULL) { pMemoryPage->Next = NULL; m_pHeadPage = pMemoryPage; } else { pMemoryPage->Next = m_pHeadPage; m_pHeadPage = pMemoryPage; } return true; } int CFixMemoryPool::GetMemUsed() { int used = 0; const int PAGE_SIZE = sizeof(MemoryPage) + m_nPageSize; MemoryPage* pMemoryPage = m_pHeadPage; while (pMemoryPage != NULL) { pMemoryPage = pMemoryPage->Next; used += PAGE_SIZE; } return used; } void CFixMemoryPool::Clear() { MemoryPage* pMemoryPage = m_pHeadPage; while (pMemoryPage != NULL) { InitPage(pMemoryPage); pMemoryPage = pMemoryPage->Next; } } void CFixMemoryPool::InitPage(MemoryPage *Page) { Page->nFreecount = m_nPageSize / m_nUnitSize; Page->nFreeHead = 0; void* head = GetPageBuf(Page); for (int i = 1; i < Page->nFreecount; ++i) { *(int*)head = i; head = (int*)((char*)head + m_nUnitSize); } } bool CFixMemoryPool::Create( unsigned int UnitSize, unsigned int PageSize /*= 0x40000*/ ) { if (UnitSize < 4) { m_nUnitSize = 4; } { m_nUnitSize = (UnitSize + (ALIGNMENT-1)) & ~(ALIGNMENT-1); } if (PageSize < MIN_PAGESIZE) { m_nPageSize = MIN_PAGESIZE; } else { m_nPageSize = (PageSize / m_nUnitSize) * m_nUnitSize; } return AddMemoryPage(); }