实现背景:malloc在申请空间的时候会有很多缺陷,比如:效率比较低下,容易造成内存碎片,也容易造成空间额外的浪费,如果用户自己释放空间操作不当,还会产生内存泄漏的问题
实现方法:若用户申请的空间大于128字节,定为大块内存,采用一级空间配置器的处理方法
若用户申请的空间小于等于128字节,定为小块内存,采用二级空间配置器的处理方法
具体实现原理:
一级空间配置器:对malloc和new的封装+空间不足的应对措施
二级空间配置器:采用内存池技术+小块内存空间的管理效率
结果显示:
STL_Alloc.hpp
#pragma once
#include <new>
#include <string>
#include <stdarg.h>
#define _DEBUG_
#ifdef _DEBUG_
static std::string GetFileName(const std::string& path)
{
char ch = '/';
#ifdef _WIN32
ch = '\\';
#endif
size_t pos = path.rfind(ch);
if (pos == std::string::npos)
return path;
else
return path.substr(pos + 1);
}
#endif
// 用于调试追踪的trace log
inline static void _trace_debug(const char * funcName,
const char * fileName, int line, char* format, ...)
{
#ifdef _DEBUG_
fprintf(stdout, "[%s:%d]%s", GetFileName(fileName).c_str(), line, funcName);
// 输出用户信息
va_list args;
va_start(args, format);
vfprintf(stdout, format, args);
va_end(args);
#endif
}
#define __TRACE_DEBUG(...) \
_trace_debug(__FUNCDNAME__, __FILE__, __LINE__, __VA_ARGS__);
typedef void(*POOM)();
// 一级空间配置器: 大于128字节的空间
// 原理:malloc + free 封装 考虑内存空间不足
template<int inst>
class MallocAllocTemplate
{
public:
static void* Allocate(size_t size)
{
void* res = malloc(size);
if (NULL == res)
{
// 系统空间不足
return OOM_Malloc(size);
}
return res;
}
static void DeAllocate(void* p, size_t/* size*/)
{
free(p);
}
// OOM: out of memory
static void* OOM_Malloc(size_t size)
{
for (;;)
{
// 检测是否设置空间不足的应对措施(函数)
// 一种简单方式:用户自己去检测程序中是否存在从堆上申请但不用的内存空间
// 只能有用户来提供
if (NULL == _pOOMFunc)
throw std::bad_alloc();
else
{
_pOOMFunc(); // 空间不足的应对措施
void* res = malloc(size);
if (res)
return res;
}
}
}
static POOM SetHandle(POOM pOOM)
{
POOM old = _pOOMFunc;
_pOOMFunc = pOOM;
return old;
}
private:
static POOM _pOOMFunc;
};
template<int inst>
POOM MallocAllocTemplate<inst>::_pOOMFunc = NULL;
typedef MallocAllocTemplate<0> MallocAlloc;
template<int inst>
class DefaultAllocateTemplate
{
enum { __ALIGN = 8 };
enum { __MAX_BYTES = 128 };
enum { __NFREELISTS = __MAX_BYTES / __ALIGN };
union Obj
{
union Obj * free_list_link;
char client_data[1];
};
public:
static void* Allocate(size_t size)
{
// 如果size超过128,调用一级空间配置器来处理
if (size > __MAX_BYTES)
{
__TRACE_DEBUG("size 大于128,一级空间配置器\n");
return MallocAlloc::Allocate(size);
}
printf("\n");
// 小块内存
__TRACE_DEBUG("size 小于128,二级空间配置器\n");
Obj* res;
size_t index = FREELIST_INDEX(size);
if (NULL == _FreeList[index])
{
__TRACE_DEBUG("二级空间配置器:_FreeList[%d]: 没有内存块\n", index);
return ReFill(ROUND_UP(size));
}
printf("\n");
__TRACE_DEBUG("二级空间配置器:_FreeList[%d]: %d\n", index, size);
res = _FreeList[index];
_FreeList[index] = res->free_list_link;
return res;
}
static void DeAllocate(void* p, size_t size)
{
if (size > __MAX_BYTES)
{
__TRACE_DEBUG("size > 128 一级空间配置器\n");
MallocAlloc::DeAllocate(p, size);
return;
}
printf("\n");
size_t index = FREELIST_INDEX(size);
((Obj*)p)->free_list_link = _FreeList[index];
_FreeList[index] = (Obj*)p;
__TRACE_DEBUG("二级空间配置器: _FreeList[%d]\n", index);
}
private:
// 想办法去找nobjs个size字节的内存块出来
static void* ChunkAlloc(int& nobjs, size_t size)
{
size_t totalBytes = nobjs * size;
size_t leftBytes = _endFree - _startFree;
void* res;
if (leftBytes >= totalBytes)
{
__TRACE_DEBUG("二级空间配置器:内存池可以提供%d\n", nobjs);
// 内存池空间比较充足,可以提供nobjs块
res = _startFree;
_startFree += totalBytes;
return res;
}
else if (leftBytes >= size)
{
// 内存池稍微吃紧,没有办法提供nobjs块,但是至少可以提供一块
nobjs = leftBytes / size;
res = _startFree;
_startFree += nobjs*size;
__TRACE_DEBUG("二级空间配置器:内存池可以提供%d\n", nobjs);
return res;
}
else
{
__TRACE_DEBUG("二级空间配置器:内存池剩余空间不足,连一块也无法提供\n");
// 内存池连一块空间都无法提供,内存池所剩余字节数< size
size_t index = FREELIST_INDEX(leftBytes);
if (leftBytes > 0)
{
__TRACE_DEBUG("二级空间配置器:将内存池中剩余的空间%d挂到_FreeList[%d]\n", leftBytes, index);
// 将内存池中剩余的空间找一个合适的链表链接起来
((Obj*)_startFree)->free_list_link = _FreeList[index];
_FreeList[index] = (Obj*)_startFree;
}
size_t getBytes = totalBytes * 2 + ROUND_UP(_heapSize >> 4);
// 1. 超系统要空间
_startFree = (char*)malloc(getBytes);
__TRACE_DEBUG("二级空间配置器:像系统索要内存空间%d\n", getBytes);
if (NULL == _startFree)
{
__TRACE_DEBUG("二级空间配置器:系统内存空间不足,在还哈希桶中找更大的内存块\n");
// 系统空间不足
for (int i = index; i < __MAX_BYTES / __ALIGN; ++i)
{
// 哈希桶中找是否有更大的内存块
if (_FreeList[i])
{
__TRACE_DEBUG("二级空间配置器:在哈希桶中找的内存块为_FreeList[%d]\n", index);
_startFree = (char*)_FreeList[i];
_FreeList[i] = _FreeList[i]->free_list_link;
_endFree = _startFree + (i + 1)*__ALIGN;
return ChunkAlloc(nobjs, size);
}
}
// 也没有更大的内存块
__TRACE_DEBUG("二级空间配置器:在哈希桶中没有找到,向一级空间配置器索要\n");
_endFree = 0;
_startFree = (char*)MallocAlloc::Allocate(size);
}
_endFree = _startFree + getBytes;
_heapSize += getBytes;
return ChunkAlloc(nobjs, size);
}
}
static void* ReFill(size_t size)
{
int nobjs = 20;
char* p = (char*)ChunkAlloc(nobjs, size);
if (nobjs == 1)
{
__TRACE_DEBUG("二级空间配置器:从内存池中只要到1块内存\n");
return p;
}
__TRACE_DEBUG("二级空间配置器:从内存池中要到%个内存块\n", nobjs);
void* res = p;
p += size;
// 将剩余的nobjs-1块的小块内存挂到对应的链表中
size_t index = FREELIST_INDEX(size);
Obj* cur = NULL;
int i = 1;
while (i < nobjs)
{
cur = (Obj*)p;
cur->free_list_link = _FreeList[index];
_FreeList[index] = cur;
p += size;
i++;
}
return res;
}
static size_t ROUND_UP(size_t bytes)
{
return (((bytes)+__ALIGN - 1) & ~(__ALIGN - 1));
}
static size_t FREELIST_INDEX(size_t bytes)
{
return (((bytes)+__ALIGN - 1) / __ALIGN - 1);
}
private:
static Obj* _FreeList[__NFREELISTS];
static char* _startFree; // 内存池的起始位置
static char* _endFree; // 内存池的结束位置
static size_t _heapSize; // 总共向系统堆索要空间的总大小
};
template<int inst>
char* DefaultAllocateTemplate<inst>::_startFree = 0;
template<int inst>
char* DefaultAllocateTemplate<inst>::_endFree = 0;
template<int inst>
size_t DefaultAllocateTemplate<inst>::_heapSize = 0;
template<int inst>
typename DefaultAllocateTemplate<inst>::Obj* DefaultAllocateTemplate<inst>::_FreeList[__NFREELISTS] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
typedef DefaultAllocateTemplate<0> DefAlloc;
#ifdef USE_MALLOC
typedef MallocAlloc _Alloc;
#else
typedef DefAlloc _Alloc;
#endif
template<class T, class Alloc>
class SimpleAlloc
{
public:
static T* Allocate(size_t n)
{
return (0 == n) ? 0 : Alloc::Allocate(n * sizeof(T));
}
static T* Allocate()
{
return Alloc::Allocate(sizeof(T));
}
static void DeAllocate(T* p, size_t n)
{
Alloc::DeAllocate(p, n * sizeof(T));
}
static void DeAllocate(T* p)
{
Alloc::DeAllocate(p, sizeof(T));
}
};
void TestAlloc()
{
char* p1 = (char*)_Alloc::Allocate(200);
_Alloc::DeAllocate(p1, 200);
char* p2 = (char*)_Alloc::Allocate(30);
char* p3 = (char*)_Alloc::Allocate(40);
char* p4 = (char*)_Alloc::Allocate(25);
char* p5 = (char*)_Alloc::Allocate(20);
_Alloc::DeAllocate(p2, 30);
_Alloc::DeAllocate(p3, 40);
_Alloc::DeAllocate(p4, 25);
_Alloc::DeAllocate(p5, 20);
}
STL_Construct.hpp
#pragma once
template <class T>
inline void Destroy(T* pointer)
{
pointer->~T();
}
template <class T1, class T2>
inline void Construct(T1* p, const T2& value)
{
new (p)T1(value);
}
Test.cpp
#include"STL_Alloc.hpp"
#include<windows.h>
int main()
{
TestAlloc();
system("pause");
return 0;
}