模拟实现空间配置器

实现背景: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;
}

猜你喜欢

转载自blog.csdn.net/ZY_20181010/article/details/82686340