C++的程序都会对内存管理比较敏感,经常会发生内存泄漏的bug,同时为了内存的快速申请和释放,以及减少小块内存碎片,故大多都会有内存管理模块。
我在自己的服务器框架内加了内存管理,基本上是STL库的那一套,对小于128个字节的内存在内存池中管理申请和释放,而大于128的调用系统的内存申请及释放, 只不过在内部添加内存记录模块,当开启DEBUG标记的时候,会记录当前内存池总大小,已使用大小,和剩余大小。 需要检测或者查找申请记录的时候可以把当前内存情况输出到文件中。
// 这里简单讲下原理实现:
内存池中管理着128个字节以下的内存申请,如果大于128个字节的内存申请直接调用系统的malloc,如果小于128个字节就在内存池中申请。而内存池中维护着16个链表,每个链表里是已经申请分配好的内存块,他们的颗粒度是8字节,比如第一个链表里全部是8个字节大小的内存块,而第二个链表里都是16个字节的内存块,第三个链表里都是24个字节的内存块,以此类推直到128个字节就是第十六个链表。当业务层面要申请字节大小为1到8个的内存时,都会在第一个链表中把已经预分配好的内存节点取出来返回给业务层。 要申请大小为9到16的内存时就把第二个链表中取出一个节点分配出去。 而如果业务层释放内存的时候,就按照对应的大小插入的相应的链表中。如果链表空了,就从预先申请的一块大的内存中,再次分配N个相应大小的内存块插入到链表中。如果预先申请的大内存也分配完了,就再次调用系统的malloc 申请一块大内存。 所有的内存节点都是在这个大的内存块上分配的。这样避免了小块的内存碎片。
而DEBUG 模式开启的话,会在申请内存的时候记录申请的所在的文件,以及行号,用于排查问题,以及统计每个模块内存使用情况。
// 贴一下 :内存池头文件代码
// 8字节对齐(颗粒度)
#define ALIGN 8
// 在池中分配的最大大小
#define MAX_SIZE 128
// 内存池中类别个数 (MAX_SIZE / ALIGN)
#define TYPE_COUNT 16
// 8字节辅助偏移量
#define ALIGN_OFFSET 3
// 一次申请的内存大小
#define ALLOC_MAX_SIZE 65536
// 一次预分配的内存个数
#define ONE_ALLOC_COUNT 32
// 内存池
class CMemoryPool
{
private:
// 内存结点
struct mem_node_t
{
union
{
mem_node_t* next;
void* data;
};
};
// 内存桶节点
struct mem_chunk_t
{
mem_chunk_t* next;
size_t size;
};
// 内存监控节点 监控内存使用情况
struct mem_check_t
{
public:
mem_check_t(void* p, size_t n, const char* name, int line):
pMemory(p),nSize(n),nLine(line),fileName(name) {}
mem_check_t(const mem_check_t& cpy) :
pMemory(cpy.pMemory), nSize(cpy.nSize), nLine(cpy.nLine),
fileName(cpy.fileName) {}
void* pMemory;
size_t nSize;
int nLine;
std::string fileName;
};
struct mem_check_t1
{
int useSize = 0;
int allocCount = 0;
int freeCount = 0;
};
public:
CMemoryPool();
virtual ~CMemoryPool();
public:
// 申请内存
void* Alloc(size_t size);
// 释放内存
void Free(void* pData, size_t size);
// 申请内存
void* AllocDebug(size_t size, const char* fileName, int nLine);
// 释放内存
void FreeDebug(void* pData, size_t size);
public:
// 设置DEBUG 模式(开启