相对于在栈空间分配内存,堆中分配内存其实是非常缓慢的。
另外,由于堆中分配的内存,需要开发者编码回收,当系统非常庞大时,容易出现分配的内容没有回收导致内存泄露的现象。
因此,许多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
内存池cpp文件
#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();
}