模擬實現空間配置器
阿新 • • 發佈:2019-01-01
實現背景: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; }