1. 程式人生 > >模擬實現空間配置器

模擬實現空間配置器

實現背景: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;
}