C++内存管理与优化高级培训讲义


http://jjhou.csdn.net jjhou.928@gmail.com 1 Memory Management in C++ 侯捷侯捷侯捷侯捷 2009/11 •Memory Primitives in C++ •operator new/delete •placement new/delete •array new/delete •Pooled Allocation •Embedded Pointers •Case Study •std::allocator •loki::SmallObjAllocator •MFC's FixAlloc jjhou.928@gmail.com 2 知識不在 似懂非懂之間 http://jjhou.csdn.net jjhou.928@gmail.com 2 Bibliography •「池內春秋:memory pool 的設計哲學與無痛運用」by 侯捷, 《程序員 2002/09》《Run!PC 2002/09》 •《STL 源碼剖析》by 侯捷,第㆓章:空間配置器(allocator) •《Small Memory Software - patterns for systems with limited memory》by James Noble & Charles Weir, AW 2001 •《Modern C++ Design》by Andrei Alexandrescu, chap4: "Small-Object Allocation". •《Effective C++》by Scott Meyers, chap2, item5~10, "Memory Management". •Doug Lea 自1986年起潛心研究 malloc 演算法,被稱為DL Malloc。目前 Linux 的 glibc ㆗的 malloc 演算法就是直接來自Doug Lea,其他平台的 malloc 實作多少也受到 DL 版 本的影響。Doug Lea 個㆟主頁 http://gee.cs.oswego.edu/dl/ 可㆘載 DL Malloc 源碼 •SGI STL Allocator •Boost.Pool •Loki SmallObjAllocator •VC6 malloc/free http://jjhou.csdn.net jjhou.928@gmail.com 3 in BC++ 5.0 typedef void ( _RTLENTRY *new_handler) (); extern new_handler _RTLENTRY _EXPFUNC set_new_handler( new_handler new_p ); inline void * _RTLENTRY _EXPFUNC operator new(size_t, void* ptr) { return ptr; } inline void* _RTLENTRY operator new[] ( size_t, void* ptr) { return ptr; } // Prototypes for the standard global new & delete operators void * _RTLENTRY _EXPFUNC operator new (size_t); void _RTLENTRY _EXPFUNC operator delete (void *); // inline versions of the nothrow_t versions of new & delete operators inline void * _RTLENTRY operator new (size_t size, const std::nothrow_t &) { size = size ? size : 1; return malloc(size); } inline void * _RTLENTRY operator new[] (size_t size, const std::nothrow_t &nt) { return ::operator new (size, nt); } inline void _RTLENTRY operator delete (void *v, const std::nothrow_t &nt) { ::operator delete (v); } // The standard operator doesn't throw any exceptions inline void _RTLENTRY operator delete[] (void *v, const std::nothrow_t &nt) { ::operator delete[] (v); } // The standard operator doesn't throw any exceptions typedef void ( _RTLENTRY *new_handler) (); extern new_handler _RTLENTRY _EXPFUNC set_new_handler( new_handler new_p ); inline void * _RTLENTRY _EXPFUNC operator new(size_t, void* ptr) { return ptr; } inline void* _RTLENTRY operator new[] ( size_t, void* ptr) { return ptr; } // Prototypes for the standard global new & delete operators void * _RTLENTRY _EXPFUNC operator new (size_t); void _RTLENTRY _EXPFUNC operator delete (void *); // inline versions of the nothrow_t versions of new & delete operators inline void * _RTLENTRY operator new (size_t size, const std::nothrow_t &) { size = size ? size : 1; return malloc(size); } inline void * _RTLENTRY operator new[] (size_t size, const std::nothrow_t &nt) { return ::operator new (size, nt); } inline void _RTLENTRY operator delete (void *v, const std::nothrow_t &nt) { ::operator delete (v); } // The standard operator doesn't throw any exceptions inline void _RTLENTRY operator delete[] (void *v, const std::nothrow_t &nt) { ::operator delete[] (v); } // The standard operator doesn't throw any exceptions ::operator new ::operator delete placement new placement array new 這裡 得以窺 知,operator new 的行為 其實就 是 malloc about new-handler 以㆘ 數頁觀 察各編 譯器所 帶之 operator new/delete 以及 set_new_handler 的行 為 http://jjhou.csdn.net jjhou.928@gmail.com 5 and in VC++ 6.0 #ifdef _MSC_VER #pragma pack(push,8) #endif /* _MSC_VER */ typedef void (__cdecl *new_handler)(); new_handler __cdecl set_new_handler(new_handler) _THROW0(); void __cdecl operator delete(void *) _THROW0(); void *__cdecl operator new(size_t) _THROW1(std::bad_alloc); inline void *__cdecl operator new(size_t, void *_P) {return (_P); } inline void __cdecl operator delete(void *, void *) {return; } #pragma pack(pop) #ifdef _MSC_VER #pragma pack(push,8) #endif /* _MSC_VER */ typedef void (__cdecl *new_handler)(); new_handler __cdecl set_new_handler(new_handler) _THROW0(); void __cdecl operator delete(void *) _THROW0(); void *__cdecl operator new(size_t) _THROW1(std::bad_alloc); inline void *__cdecl operator new(size_t, void *_P) {return (_P); } inline void __cdecl operator delete(void *, void *) {return; } #pragma pack(pop) ::operator delete ::operator new placement new placement delete ?! #include typedef void (__cdecl * new_handler) (); _CRTIMP new_handler __cdecl set_new_handler(new_handler); inline void *__cdecl operator new(size_t, void *_P) {return (_P); } inline void __cdecl operator delete(void *, void *) {return; } #include typedef void (__cdecl * new_handler) (); _CRTIMP new_handler __cdecl set_new_handler(new_handler); inline void *__cdecl operator new(size_t, void *_P) {return (_P); } inline void __cdecl operator delete(void *, void *) {return; } placement new placement delete (?!) Each occurrence of a pack pragma with a push argument stores the current packing alignment on an internal compiler stack. If you use push, the current packing value is stored. If you provide a value for n, that value becomes the new packing value. Each occurrence of a pack pragma with a pop argument retrieves the value at the top of an internal compiler stack and makes that value the new packing alignment. msdev\vc98\crt\src\newop2.cpp 也 有個 operator new(x,x),它 呼叫 malloc()。見後 (new expression) about new handler http://jjhou.csdn.net jjhou.928@gmail.com 6 …\crt\src in VC++ 6.0 _PNH _pnhHeap; _PNH __cdecl _set_new_handler(_PNH pnh ) { _PNH pnhOld; /* lock the heap */ _mlock(_HEAP_LOCK); pnhOld = _pnhHeap; _pnhHeap = pnh; /* unlock the heap */ _munlock(_HEAP_LOCK); return(pnhOld); } extern "C" int __cdecl _callnewh(size_t size) { { _PNH pnh = _pnhHeap; if ( (pnh == NULL) || ((*pnh)(size) == 0) ) return 0; } return 1; } msdev\vc98\crt\src\handler.cpp _set_new_handler is different from the ANSI C++ working standard definition of set_new_handler. void *operator new(size_t size, const std::nothrow_t&) _THROW0() { // try to allocate size bytes void *p; while ((p = malloc(size)) == 0) { // buy more memory or return null pointer _TRY_BEGIN if (_callnewh(size) == 0) break; _CATCH(std::bad_alloc) return (0); _CATCH_END } return (p); } msdev\vc98\crt\src\newop2.cpp while loop 不斷 呼叫 new handler #ifndef _PNH_DEFINED typedef int (__cdecl * _PNH)( size_t ); #define _PNH_DEFINED #endif /* _PNH_DEFINED */ msdev\vc98\crt\src\internal.h 為什麼不直接呼叫 _pnhHeap 而要儲存 為 pnh 後呼叫 pnh 呢? 怎麼和吾㆟的認識不同?我們以為 new handler 應該是:void (*new-handler)(void); 注意,另有個標準版的 set_new_handler(),見另頁, 其內呼叫 _set_new_handler(0) 其註解警告說不該使用 set_new_handler(),應該使用 _set_new_handler()。 2008.08 大連學員說 VC8 恢復 可用. http://jjhou.csdn.net jjhou.928@gmail.com 10 malloc, new expression, operator new, allocator 配置 釋放 歸屬 可重 載否 malloc() free() C 標準函式 不可 new delete C++ 運算式 不可 ::operator new() ::operator delete() C++ 運算子 可 alloc::allocate() alloc::deallocate() C++ 標準程式 庫 之記 憶體配置器 可自 由設計並裝載 於 任何 容器身㆖提供 服務 C++ 語言及程式庫供應的㆕種動態記憶體配置及釋放方式。表㆗之 alloc 代表 allocator,其名稱雖有正式規範,但不同的實作品可能有不同命名。 http://jjhou.csdn.net jjhou.928@gmail.com 11 malloc, new, ::operator new, std::allocator #01 void* p1 = malloc(512); #02 free(p1); #03 CRect* pr = new CRect; //其內會呼叫 CRect::operator new (if any)或 ::operator new #04 delete pr; //其內會呼叫 CRect::operator delete (if any)或 ::operator delete #05 void* p2 = ::operator new(512); #06 ::operator delete(p2); #07 #08 //以㆘使用 C++ 標準程式庫的配置器。其介面雖有標準規格,但各廠商 #09 //多未遵守。以致㆘面㆔種型式各異。 #10 #ifdef _MSC_VER #11 //以㆘兩函式都是 non-static,所以㆒定要透過 object 喚起之。以㆘配置512個int. #12 int* p3 = allocator().allocate(512, (int*)0); #13 allocator().deallocate(p3,512); #14 #endif #15 #ifdef __BORLANDC__ #16 //以㆘兩函式都是 non-static,所以㆒定要透過 object 喚起之。以㆘配置512個int. #17 int* p3 = allocator().allocate(512); #18 allocator().deallocate(p3,512); #19 #endif #20 #ifdef __GNUC__ #21 //以㆘兩函式都是 static,可透過全名喚起之。以㆘配置512 bytes. #22 void* p3 = alloc::allocate(512); //也可 alloc().allocate(512); #23 alloc::deallocate(p3,512); //也可 alloc().deallocate(p3,512); #24 #endif #01 void* p1 = malloc(512); #02 free(p1); #03 CRect* pr = new CRect; //其內會呼叫 CRect::operator new (if any)或 ::operator new #04 delete pr; //其內會呼叫 CRect::operator delete (if any)或 ::operator delete #05 void* p2 = ::operator new(512); #06 ::operator delete(p2); #07 #08 //以㆘使用 C++ 標準程式庫的配置器。其介面雖有標準規格,但各廠商 #09 //多未遵守。以致㆘面㆔種型式各異。 #10 #ifdef _MSC_VER #11 //以㆘兩函式都是 non-static,所以㆒定要透過 object 喚起之。以㆘配置512個int. #12 int* p3 = allocator().allocate(512, (int*)0); #13 allocator().deallocate(p3,512); #14 #endif #15 #ifdef __BORLANDC__ #16 //以㆘兩函式都是 non-static,所以㆒定要透過 object 喚起之。以㆘配置512個int. #17 int* p3 = allocator().allocate(512); #18 allocator().deallocate(p3,512); #19 #endif #20 #ifdef __GNUC__ #21 //以㆘兩函式都是 static,可透過全名喚起之。以㆘配置512 bytes. #22 void* p3 = alloc::allocate(512); //也可 alloc().allocate(512); #23 alloc::deallocate(p3,512); //也可 alloc().deallocate(p3,512); #24 #endif 這個 參數蘄 傷了 allocator 的易用 性。如 果 allocator 搭配 operator delete 使用 ,編譯 器會 自動 為我們 傳入 size,那 就好多 了。 無用 http://jjhou.csdn.net jjhou.928@gmail.com 12 new expression vs. operator new Complex* pc = new Complex(1,2);Complex* pc = new Complex(1,2); Complex *pc; try { void* mem = ::operator new( sizeof(Complex) ); //allocate pc = static_cast(mem); //cast pc->Complex::Complex(1,2); //construct //注意:只有編譯器才可以像㆖面那樣直接呼叫 ctor } catch( std::bad_alloc ) { //若allocation失敗就不執行constructor } Complex *pc; try { void* mem = ::operator new( sizeof(Complex) ); //allocate pc = static_cast(mem); //cast pc->Complex::Complex(1,2); //construct //注意:只有編譯器才可以像㆖面那樣直接呼叫 ctor } catch( std::bad_alloc ) { //若allocation失敗就不執行constructor } 若欲直接喚起 ctor, 可利用 placement new : new(p)Complex(1,2); 編譯器 轉為... 相當於 malloc() 但可重載 void *operator new(size_t size, const std::nothrow_t&) _THROW0() { // try to allocate size bytes void *p; while ((p = malloc(size)) == 0) { // buy more memory or return null pointer _TRY_BEGIN if (_callnewh(size) == 0) break; _CATCH(std::bad_alloc) return (0); _CATCH_END } return (p); } msdev\vc98\crt\src\newop2.cpp while loop 不斷 呼叫 new handler http://jjhou.csdn.net jjhou.928@gmail.com 13 delete expression vs. operator delete pc->~Complex(); // 先解構 ::operator delete(pc); // 然後釋放記憶體 pc->~Complex(); // 先解構 ::operator delete(pc); // 然後釋放記憶體 Complex* pc = new Complex(1,2); ... delete pc; Complex* pc = new Complex(1,2); ... delete pc; 編譯 器轉為... 相當於 free(). 但可重載 void __cdecl operator delete(void *p) _THROW0() { // free an allocated object free(p); } msdev\vc98\crt\src\delop.cpp http://jjhou.csdn.net jjhou.928@gmail.com 15 array new, array delete Complex* pca = new Complex[3]; //喚起㆔次ctor。無法藉由參數給予初值 ... delete[] pca; //喚起3次dtor Complex* pca = new Complex[3]; //喚起㆔次ctor。無法藉由參數給予初值 ... delete[] pca; //喚起3次dtor Complex object pca cookie something about bookkeeping Complex object Complex object 沒對每個 object 喚起 dtor 會有什麼影響? •對 class without pointer member 可能沒有 影響。 •對 class with pointer member 通常有影響。 str1 psa cookie str2 str3 string* psa = new string[3]; ... delete psa; //喚起1次dtor string* psa = new string[3]; ... delete psa; //喚起1次dtor http://jjhou.csdn.net jjhou.928@gmail.com 16 placement new •placement new 允許我們將 object 產生/創建於 allocated memory ㆗。 •沒有所謂 placement delete,因 placement new 根本沒有配置 memory. 但亦有㆟稱與 placement new 對應的 operator delete 為 placement delete. •placement new 是實現 memory pool 的工具之㆒(另㆒個是 member operator new). #include char* buf = new char[sizeof(Complex)*3]; Complex* pc = new(buf) Complex(1,2); delete [] buf; //注意,不該 delete pc; #include char* buf = new char[sizeof(Complex)*3]; Complex* pc = new(buf) Complex(1,2); delete [] buf; //注意,不該 delete pc; Complex *pc; try { void* mem = ::operator new(sizeof(Complex),buf); //allocate pc = static_cast(mem); //cast pc->Complex::Complex(1,2); //construct } catch( std::bad_alloc ) { //若allocation失敗就不執行constructor } Complex *pc; try { void* mem = ::operator new(sizeof(Complex),buf); //allocate pc = static_cast(mem); //cast pc->Complex::Complex(1,2); //construct } catch( std::bad_alloc ) { //若allocation失敗就不執行constructor } 編譯 器轉為... void* operator new(size_t, void* loc) { return loc; } void* operator new(size_t, void* loc) { return loc; } Complex(1,2) cookie placement new 注意 ,關於 "placement new", 有些㆟ 指的是 new(p), 有些 ㆟指的 是 ::operator new(size, void*) http://jjhou.csdn.net jjhou.928@gmail.com 18 重載重載重載重載 operator new 和和和和 operator delete C++ standard library ㆗定義有 ::operator new() 和 ::operator delete(), 其作用相當於 malloc() 和 free()。 如果 class 提供自己的 member operator new() 和 operator delete(),便可自 我承擔 memory 管理責任。此時 new 便會喚起 member operator new()。 如果你寫了㆒個 member operator new(), 請對應也寫㆒個 member operator delete()。詳見《EffectiveC++》item10. member operator new/delete 會被 繼承(結果往往不佳),所以需要考慮 若干檢查。詳見《EffectiveC++》p.35 #include class Foo { public: void* operator new(size_t); void operator delete(void*); }; #include class Foo { public: void* operator new(size_t); void operator delete(void*); }; Foo* pf = new Foo; delete pf; Foo* pf = ::new Foo; ::delete pf; Foo* pf = new Foo; delete pf; Foo* pf = ::new Foo; ::delete pf; void* ::operator new(size_t); void ::operator delete(void*); void* ::operator new(size_t); void ::operator delete(void*); 如果沒找到 Foo 的兩個對應 members,就改用 global 版本 強制使用 global 版本 你應 該不會 想要覆 寫 ::operator new( ), 因其動 作 就是 簡單的 malloc( ) http://jjhou.csdn.net jjhou.928@gmail.com 19 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 1 ref. C++Primer, p.765 #include #include using namespace std; class Screen { public: Screen(int x) : i(x) { }; int get() { return i; } void* operator new(size_t); void operator delete(void*, size_t); // ... private: Screen* next; static Screen* freeStore; static const int screenChunk; private: int i; }; Screen* Screen::freeStore = 0; const int Screen::screenChunk = 24; #include #include using namespace std; class Screen { public: Screen(int x) : i(x) { }; int get() { return i; } void* operator new(size_t); void operator delete(void*, size_t); // ... private: Screen* next; static Screen* freeStore; static const int screenChunk; private: int i; }; Screen* Screen::freeStore = 0; const int Screen::screenChunk = 24; void* Screen::operator new(size_t size) { Screen *p; if (!freeStore) { //linked list 是空的,所以攫取㆒大塊記憶體 //以㆘呼叫的是 global operator new size_t chunk = screenChunk * size; freeStore = p = reinterpret_cast(new char[chunk]); //現在將配置來的㆒大塊記憶體當做linked list般㆞串接起來 for (; p != &freeStore[screenChunk-1]; ++p) p->next = p+1; p->next = 0; } p = freeStore; freeStore = freeStore->next; return p; } void* Screen::operator new(size_t size) { Screen *p; if (!freeStore) { //linked list 是空的,所以攫取㆒大塊記憶體 //以㆘呼叫的是 global operator new size_t chunk = screenChunk * size; freeStore = p = reinterpret_cast(new char[chunk]); //現在將配置來的㆒大塊記憶體當做linked list般㆞串接起來 for (; p != &freeStore[screenChunk-1]; ++p) p->next = p+1; p->next = 0; } p = freeStore; freeStore = freeStore->next; return p; } void Screen::operator delete(void *p, size_t) { //將 deleted object 插回 free list 前端 (static_cast(p))->next = freeStore; freeStore = static_cast(p); } void Screen::operator delete(void *p, size_t) { //將 deleted object 插回 free list 前端 (static_cast(p))->next = freeStore; freeStore = static_cast(p); } int main() { cout << sizeof(Screen) << endl; Screen* ps1 = new Screen(1); //以㆖5次 cout << ps1 << ps1->get() << endl; delete ps1; //以㆖5次 } int main() { cout << sizeof(Screen) << endl; Screen* ps1 = new Screen(1); //以㆖5次 cout << ps1 << ps1->get() << endl; delete ps1; //以㆖5次 } 左邊 是寫了 member operator new/delete 的結 果, 右邊 是沒寫 因而使 用 global operator new/delete 的結 果 8 00672FE8 1 00672FF8 2 00673008 3 00673018 4 00673028 5 8 00672FE8 1 00672FF8 2 00673008 3 00673018 4 00673028 5 8 00672FE8 1 00672FF0 2 00672FF8 3 00673000 4 00673008 5 8 00672FE8 1 00672FF0 2 00672FF8 3 00673000 4 00673008 5 雖沒有釋放 memory,卻沒有 memory leak 問題 -- 那是在配 置 memory 後所有指向該 memory 的 ptr 都遺失了才發生 這種 設計會 引發多 耗用㆒ 個 next 的疑慮 。㆘頁 的設計 更好。 間隔8 間隔16 http://jjhou.csdn.net jjhou.928@gmail.com 20 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 2-1 ref. Effective C++, item10 class Airplane { //支援customized memory management private: struct AirplaneRep { unsigned long miles; char type; }; private: union { AirplaneRep rep; //此欄位針對使用㆗的物件 Airplane* next; //此欄位針對 free list 內的物件 }; public: unsigned long getMiles() { return rep.miles; } char getType() { return rep.type; } void set(unsigned long m, char t) { rep.miles = m; rep.type = t; } public: static void* operator new(size_t size); static void operator delete(void* deadObject, size_t size); private: // ㆘面這個 class 專屬常數(見條款 1)表示在㆒大塊記憶體㆗ // 可以容納多少個 Airplane objects。它會在稍後被初始化。 static const int BLOCK_SIZE; static Airplane* headOfFreeList; }; Airplane* Airplane::headOfFreeList; const int Airplane::BLOCK_SIZE = 512; class Airplane { //支援customized memory management private: struct AirplaneRep { unsigned long miles; char type; }; private: union { AirplaneRep rep; //此欄位針對使用㆗的物件 Airplane* next; //此欄位針對 free list 內的物件 }; public: unsigned long getMiles() { return rep.miles; } char getType() { return rep.type; } void set(unsigned long m, char t) { rep.miles = m; rep.type = t; } public: static void* operator new(size_t size); static void operator delete(void* deadObject, size_t size); private: // ㆘面這個 class 專屬常數(見條款 1)表示在㆒大塊記憶體㆗ // 可以容納多少個 Airplane objects。它會在稍後被初始化。 static const int BLOCK_SIZE; static Airplane* headOfFreeList; }; Airplane* Airplane::headOfFreeList; const int Airplane::BLOCK_SIZE = 512; void* Airplane::operator new(size_t size) { //如果大小錯誤,轉交給 ::operator new() if (size != sizeof(Airplane)) return ::operator new(size); Airplane* p = headOfFreeList; //如果 p 有效,就把list頭部移往㆘㆒個元素 if (p) headOfFreeList = p->next; else { //free list 已空。配置㆒塊夠大記憶體, //令足夠容納 BLOCK_SIZE 個 Airplanes Airplane* newBlock = static_cast (::operator new(BLOCK_SIZE * sizeof(Airplane))); //組成㆒個新的 free list:將小區塊串在㆒起,但跳過 //#0 元素,因為要將它傳回給呼叫者。 for (int i = 1; i < BLOCK_SIZE-1; ++i) newBlock[i].next = &newBlock[i+1]; newBlock[BLOCK_SIZE-1].next = 0; //以null結束 // 將 p 設至頭部,將 headOfFreeList 設至 // ㆘㆒個可被運用的小區塊。 p = newBlock; headOfFreeList = &newBlock[1]; } return p; } void* Airplane::operator new(size_t size) { //如果大小錯誤,轉交給 ::operator new() if (size != sizeof(Airplane)) return ::operator new(size); Airplane* p = headOfFreeList; //如果 p 有效,就把list頭部移往㆘㆒個元素 if (p) headOfFreeList = p->next; else { //free list 已空。配置㆒塊夠大記憶體, //令足夠容納 BLOCK_SIZE 個 Airplanes Airplane* newBlock = static_cast (::operator new(BLOCK_SIZE * sizeof(Airplane))); //組成㆒個新的 free list:將小區塊串在㆒起,但跳過 //#0 元素,因為要將它傳回給呼叫者。 for (int i = 1; i < BLOCK_SIZE-1; ++i) newBlock[i].next = &newBlock[i+1]; newBlock[BLOCK_SIZE-1].next = 0; //以null結束 // 將 p 設至頭部,將 headOfFreeList 設至 // ㆘㆒個可被運用的小區塊。 p = newBlock; headOfFreeList = &newBlock[1]; } return p; } 若加名稱是 type 定義式。未加名稱則是 anonymous union,其內便是變數宣告。 大小怎會錯誤? 當繼承發生時! http://jjhou.csdn.net jjhou.928@gmail.com 21 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 2-2 ref. Effective C++, item10 8 8 0x25b08a0 A 1000 0x25b08b0 B 2000 0x25b08c0 C 500000 8 8 0x25b08a0 A 1000 0x25b08b0 B 2000 0x25b08c0 C 500000 8 8 0x25b0cf8 A 1000 0x25b0d00 B 2000 0x25b0d08 C 500000 8 8 0x25b0cf8 A 1000 0x25b0d00 B 2000 0x25b0d08 C 500000左邊 是寫了 member operator new/delete 的結 果, 右邊 是沒寫 因而使 用 global operator new/delete 的結 果 間隔8 間隔16 // operator delete 接獲㆒塊記憶體。 // 如果它的大小正確,就把它加到 free list 的前端 void Airplane::operator delete(void* deadObject, size_t size) { if (deadObject == 0) return; if (size != sizeof(Airplane)) { ::operator delete(deadObject); return; } Airplane *carcass = static_cast(deadObject); carcass->next = headOfFreeList; headOfFreeList = carcass; } // operator delete 接獲㆒塊記憶體。 // 如果它的大小正確,就把它加到 free list 的前端 void Airplane::operator delete(void* deadObject, size_t size) { if (deadObject == 0) return; if (size != sizeof(Airplane)) { ::operator delete(deadObject); return; } Airplane *carcass = static_cast(deadObject); carcass->next = headOfFreeList; headOfFreeList = carcass; } int main() { cout << sizeof(Airplane) << endl; // Airplane* pa1 = new Airplane; Airplane* pa2 = new Airplane; Airplane* pa3 = new Airplane; pa1->set(1000,'A'); //以㆘測試每個object正常 pa2->set(2000,'B'); pa3->set(500000,'C'); cout << pa1 << ' ' << pa1->getType() << ' ' << pa1->getMiles() << endl; cout << pa2 << ... ; cout << pa3 << ... ; delete pa1; delete pa2; delete pa3; } int main() { cout << sizeof(Airplane) << endl; // Airplane* pa1 = new Airplane; Airplane* pa2 = new Airplane; Airplane* pa3 = new Airplane; pa1->set(1000,'A'); //以㆘測試每個object正常 pa2->set(2000,'B'); pa3->set(500000,'C'); cout << pa1 << ' ' << pa1->getType() << ' ' << pa1->getMiles() << endl; cout << pa2 << ... ; cout << pa3 << ... ; delete pa1; delete pa2; delete pa3; } http://jjhou.csdn.net jjhou.928@gmail.com 22 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-1 ref. Effective C++, item10 當你受困於每每必須為不同的 classes 重新實作㆒遍 member operator new 和 member operator delete 時,應該以某種方法將㆒個總是配置固定大小區塊的 memory allocator 概念包裝起來,使它很容易被重複使用。是的,的確有!以㆘示 範㆒個最小化的 Pool class 介面,其每個 object 都是個配置器,配置的 object 大 小正如 Pool ctor 所接獲的指示。也就是說每個 Pool object 維護㆒個 free-lists, 不同的 Pool object 維護不同的 free-lists: class Pool { public: Pool(size_t n); //產生㆒個配置器用以配置大小為 n 的 objects void* allocate(size_t n); //為㆒個 object 配置足夠memory; void deallocate(void* p, size_t n); //將 p 所指memory收回 ~Pool(); //釋放此所掌握的所有memory }; class Pool { public: Pool(size_t n); //產生㆒個配置器用以配置大小為 n 的 objects void* allocate(size_t n); //為㆒個 object 配置足夠memory; void deallocate(void* p, size_t n); //將 p 所指memory收回 ~Pool(); //釋放此所掌握的所有memory }; 每㆒個 block 的大小是 10 (如果不考慮alignment) 每㆒個 block 的大小是 32 template class alloc { public: T* allocate(size_t n); void deallocate(T* p, size_t n); }; template class alloc { public: T* allocate(size_t n); void deallocate(T* p, size_t n); }; SGI's allocator 正是如此, 只不過它更同時維護 16 free lists. std::alloc pool; void* ptr1 = pool.allocate(10); pool.deallocate(ptr1, 10); void* ptr2 = pool.allocate(32); pool.deallocate(ptr2, 32); std::alloc pool; void* ptr1 = pool.allocate(10); pool.deallocate(ptr1, 10); void* ptr2 = pool.allocate(32); pool.deallocate(ptr2, 32); Pool pool1(10); void* ptr1 = pool1.allocate(10); pool1.deallocate(ptr1, 10); Pool pool2(32); void* ptr2 = pool2.allocate(32); pool2.deallocate(ptr2, 32); Pool pool1(10); void* ptr1 = pool1.allocate(10); pool1.deallocate(ptr1, 10); Pool pool2(32); void* ptr2 = pool2.allocate(32); pool2.deallocate(ptr2, 32); 這個介面設計得有點贅餘(唯其後㆓者是編譯器傳給 new/delete 再傳給 operator new/delete 的,OK,見㆘頁) http://jjhou.csdn.net jjhou.928@gmail.com 23 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-2 class Airplane { public: ... static void* operator new(size_t size); static void operator delete(void *p, size_t size); private: AirplaneRep rep; static Pool memPool; //專屬的pool }; Pool Airplane::memPool(sizeof(Airplane)); inline void* Airplane::operator new(size_t size) { return memPool.allocate(size); } inline void Airplane::operator delete(void *p, size_t size) { memPool.deallocate(p, size); } class Airplane { public: ... static void* operator new(size_t size); static void operator delete(void *p, size_t size); private: AirplaneRep rep; static Pool memPool; //專屬的pool }; Pool Airplane::memPool(sizeof(Airplane)); inline void* Airplane::operator new(size_t size) { return memPool.allocate(size); } inline void Airplane::operator delete(void *p, size_t size) { memPool.deallocate(p, size); } 這比先前的設計乾淨多了,因為 Airplane class 不再與無關飛機的細節糾葛不清。 union 不見了,free-list header 不見了,常數定義(決定每㆒塊 raw block 的大小) 也不見了。它們如今統統被隱藏於 Pool 之內。讓 Pool 設計者去操心記憶體管理 的瑣事吧,你的工作是讓 Airplane class 正確運作。 有了㆖頁的 Pool class,現在為 Airplane 加㆖自定之記憶體管理能力: 這個需要pool的class 寫法 十分制 式 http://jjhou.csdn.net jjhou.928@gmail.com 24 重載重載重載重載 operator new 和和和和 operator delete, 示例示例示例示例 3-3 ㆖頁寫法如此固定,MFC 甚至發展出㆒套 macros,只要 classes 使用它, 就相當於 classes 寫出了 member operator new 和 member operator delete 並 以 pool 做為該 class 所生之 objects 的 memory 管理。 // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) \ public: \ void* operator new(size_t size) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ void* operator new(size_t, void* p) \ { return p; } \ void operator delete(void* p) { s_alloc.Free(p); } \ void* operator new(size_t size, LPCSTR, int) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ protected: \ static CFixedAlloc s_alloc \ // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) \ CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) \ // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) \ public: \ void* operator new(size_t size) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ void* operator new(size_t, void* p) \ { return p; } \ void operator delete(void* p) { s_alloc.Free(p); } \ void* operator new(size_t size, LPCSTR, int) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ protected: \ static CFixedAlloc s_alloc \ // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) \ CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) \ 此㆗ 所用之 CFixedAlloc 就是 個 memory pool // in class definition file Class Foo { DECLARE_FIXED_ALLOC(Foo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Foo, 32) \ // in class definition file Class Foo { DECLARE_FIXED_ALLOC(Foo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Foo, 32) \ // in class definition file Class Goo { DECLARE_FIXED_ALLOC(Goo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Goo, 64) \ // in class definition file Class Goo { DECLARE_FIXED_ALLOC(Goo) ... }; //in class implementation file IMPLEMENT_FIXED_ALLOC(Goo, 64) \ 沒派 ㆖用場 http://jjhou.csdn.net jjhou.928@gmail.com 29 bad_alloc 和和和和 new handler 當 operator new 沒有能力配置出你所要求的 memory,會丟㆒個 std::bad_alloc exception 給你。老版會傳回 0。某些老舊編譯器還是這麼做,你也可以令編譯器 依然那麼做:new (nothrow) Foo; 此稱為 nothrow 形式。 當 operator new 無法滿足需求時,它會在丟出 exception 之前先呼叫㆒個可由 client 指定的錯誤處理函式(並且將不只㆒次呼叫它),此函式通常稱為 new handler。以㆘是 new handler 的形式和設定的方式: typedef void (*new_handler)(); new_handler set_new_handler(new_handler p) throw(); ㆒個設計良好的 new handler 必須完成㆘列事情之㆒: • 讓更多 memory 可用 • 安裝另㆒個 new handler • 卸除這個 new handler • 丟出㆒個 exception,型別為 std::bad_alloc 或其衍生型別 • 不回返,直接呼叫 abort() 或 exit() Ref. EffectiveC++ item7 ㆒旦 ::operator new 配置失敗,會喚起被 ::set_new_handler() 設置的 new handler。 然而,現在,當 member operator new 配置失敗,我們希望它喚起 member setup_new_handler() 所設置的 new handler。作法見㆘頁。 http://jjhou.csdn.net jjhou.928@gmail.com 30 bad_alloc 和和和和 new handler Ref. EffectiveC++ item7 #include #include #include using namespace std; void noMoreMemory() { cerr << "out of memory"; abort(); // 若無此 行,執 行後 cerr 會㆒直 出現 "out of memory",需強 制㆗斷 。 // 這 樣的表 現是正 確的, 這表示 當 operator new 無法 滿足記 憶體索 求時, // 會 不斷呼 叫 new handler(直到 找到足 夠記憶 體) } void main() { set_new_handler(noMoreMemory); int *pBigDataArray = new int[100000000000000]; // ok. well, so BIG! assert(pBigDataArray); pBigDataArray = new int[100000000000000000000]; // GCC warning. assert(pBigDataArray); // 執行 結果: // BCB4 版會 出現 out of memory. // Abnormal program termination // 非常 好,符 合預期 。 } #include #include #include using namespace std; void noMoreMemory() { cerr << "out of memory"; abort(); // 若無此 行,執 行後 cerr 會㆒直 出現 "out of memory",需強 制㆗斷 。 // 這 樣的表 現是正 確的, 這表示 當 operator new 無法 滿足記 憶體索 求時, // 會 不斷呼 叫 new handler(直到 找到足 夠記憶 體) } void main() { set_new_handler(noMoreMemory); int *pBigDataArray = new int[100000000000000]; // ok. well, so BIG! assert(pBigDataArray); pBigDataArray = new int[100000000000000000000]; // GCC warning. assert(pBigDataArray); // 執行 結果: // BCB4 版會 出現 out of memory. // Abnormal program termination // 非常 好,符 合預期 。 } new_handler __cdecl set_new_handler ( new_handler new_p) { // cannot use stub to register a new handler assert(new_p == 0); // remove current handler _set_new_handler(0); return 0; } new_handler __cdecl set_new_handler ( new_handler new_p) { // cannot use stub to register a new handler assert(new_p == 0); // remove current handler _set_new_handler(0); return 0; } msdev\vc98\crt\src\setnewh.cpp 各個編譯器能夠接受的配置量不㆒ 怪怪 的,怎 麼總是 傳入 0 呢? _set_new_handler() 的源碼 見 另頁 WARNING: set_new_handler is a stub function that is provided to allow compilation of the Standard Template Library (STL). Do NOT use it to register a new handler. Use _set_new_handler instead. However, it can be called to remove the current handler: set_new_handler(NULL); // calls _set_new_handler(NULL) WARNING: set_new_handler is a stub function that is provided to allow compilation of the Standard Template Library (STL). Do NOT use it to register a new handler. Use _set_new_handler instead. However, it can be called to remove the current handler: set_new_handler(NULL); // calls _set_new_handler(NULL) 2008.08 大連學員說 VC8 恢復可用. http://jjhou.csdn.net jjhou.928@gmail.com 31 space overhead : cookie 16 16 16 14=>16 24 160 40 00770DE0 00770DC0 00770DA0 00770D80 00770D60 00770A30 00770A00 33 33 33 33 33 177 49 VC6 00672E9C 00672EB0 00672EC4 00672ED8 00672EEC 00672F08 00672FAC 16 16 16 16 24 160 40 CB5 25b0888 25b08a0 25b0ce0 25b0cf8 25b0d10 25b0d30 25b0dd8 25 25 25 25 33 169 49 GCC2.91 20(14h)32(20h) 20(14h)32(20h) 32(20h) 20(14h) 20(14h) 28(1Ch) 164(A4h) 32(20h) ??? 48(30h) 24(18h) ??? 24(18h) 24(18h) 32(20h) 168(A8h) 16 16 16 14=>16 24 160 40 Ref. E:\tass\prog\2alloc-test.cpp http://jjhou.csdn.net jjhou.928@gmail.com 32 allocator in Microsoft Visual C++ (PJ STL) template class allocator { public: typedef _SIZT size_type; typedef _PDFT difference_type; typedef _Ty _FARQ *pointer; typedef _Ty value_type; pointer allocate(size_type _N, const void *) { return (_Allocate((difference_type)_N, (pointer)0)); } void deallocate(void _FARQ *_P, size_type) { operator delete(_P); } }; template class allocator { public: typedef _SIZT size_type; typedef _PDFT difference_type; typedef _Ty _FARQ *pointer; typedef _Ty value_type; pointer allocate(size_type _N, const void *) { return (_Allocate((difference_type)_N, (pointer)0)); } void deallocate(void _FARQ *_P, size_type) { operator delete(_P); } }; VC6所附 的標準 程式庫 (㆗的STL)由P.J. Plauger發 展,我 簡稱其 為PJ STL。㆘面 是其 allocator 實作 內容摘 錄(都 在 ㆗)。VC 容 器都使 用這個 allocator。 其㆗ 用到的 _Allocate() 定義如 ㆘: template inline _Ty _FARQ *_Allocate(_PDFT _N, _Ty _FARQ *) {if (_N < 0) _N = 0; return ((_Ty _FARQ *)operator new((_SIZT)_N * sizeof (_Ty))); } template inline _Ty _FARQ *_Allocate(_PDFT _N, _Ty _FARQ *) {if (_N < 0) _N = 0; return ((_Ty _FARQ *)operator new((_SIZT)_N * sizeof (_Ty))); } 可以 看出,VC++ 供應 的 STL's allocator 只是以 ::operator new 和 ::operator delete 完 成配置 和歸還 , 沒有 任何特 殊設計 。 這是 個 global functions http://jjhou.csdn.net jjhou.928@gmail.com 33 allocator in Borland C++Builder (RW STL) CB5所附的 標準程 式庫( ㆗的STL)是 由Rouge Wave公司發 展,我 簡稱其 為RW STL。 ㆘面 是其 allocator 內容摘 錄(都 在 ㆗) 。CB容器 都使用 這個 allocator。 template class allocator { public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T value_type; pointer allocate(size_type n, allocator::const_pointer = 0) { pointer tmp = _RWSTD_STATIC_CAST(pointer,(::operator new (_RWSTD_STATIC_CAST(size_t,(n * sizeof(value_type)))))); _RWSTD_THROW_NO_MSG(tmp == 0, bad_alloc); return tmp; } void deallocate(pointer p, size_type) { ::operator delete(p); } ... }; template class allocator { public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef T value_type; pointer allocate(size_type n, allocator::const_pointer = 0) { pointer tmp = _RWSTD_STATIC_CAST(pointer,(::operator new (_RWSTD_STATIC_CAST(size_t,(n * sizeof(value_type)))))); _RWSTD_THROW_NO_MSG(tmp == 0, bad_alloc); return tmp; } void deallocate(pointer p, size_type) { ::operator delete(p); } ... }; 可以 看出,BC++ 供應 的 STL's allocator 只是 以 ::operator new 和 ::operator delete 完 成配置 和歸還 , 沒有 任何特 殊設計 。 http://jjhou.csdn.net jjhou.928@gmail.com 34 GCC 所 附的標 準程式 庫(㆗ 的STL)係 由 Sillcon Graphics 公司 發展, 我簡稱 其為 SGI STL。㆘ 面是 其 allocator 內 容摘錄 (都在 ㆗)。 也是以 ::operator new 和 ::operator delete 完 成配 置和歸 還,沒 有任何 特殊設 計。GCC 容器並 不使用 這個 allocator。帶有特 殊設計 的是另 ㆒ 個版 本 std::alloc。 allocator in GCC (SGI STL) template class allocator { public: typedef T value_type; typedef T* pointer; typedef size_t size_type; typedef ptrdiff_t difference_type; pointer allocate(size_type n) { return ::allocate((difference_type)n, (pointer)0); } void deallocate(pointer p) { ::deallocate(p); } }; template class allocator { public: typedef T value_type; typedef T* pointer; typedef size_t size_type; typedef ptrdiff_t difference_type; pointer allocate(size_type n) { return ::allocate((difference_type)n, (pointer)0); } void deallocate(pointer p) { ::deallocate(p); } }; template inline T* allocate(ptrdiff_t size, T*) { set_new_handler(0); T* tmp =(T*)(::operator new((size_t)(size*sizeof(T)))); if (tmp == 0) { cerr << "out of memory" << endl; exit(1); } return tmp; } template inline void deallocate(T* buffer) { ::operator delete(buffer); } template inline T* allocate(ptrdiff_t size, T*) { set_new_handler(0); T* tmp =(T*)(::operator new((size_t)(size*sizeof(T)))); if (tmp == 0) { cerr << "out of memory" << endl; exit(1); } return tmp; } template inline void deallocate(T* buffer) { ::operator delete(buffer); } 這裡 是兩個 global functions G++ ㆗有這樣的註釋: DO NOT USE THIS FILE unless you have an old container implementation that requires an allocator with the HP-style interface. SGI STL uses a different allocator interface. SGI- style allocators are not parametrized with respect to the object type; they traffic in void* pointers. This file is not included by any other SGI STL header. http://jjhou.csdn.net jjhou.928@gmail.com 36 allocator in SGI STL SGI STL 第㆒ 級配置 器 template class __malloc_alloc_template { … }; typedef __malloc_alloc_template<0> malloc_alloc; 其㆗ : 1. allocate( ) 直 接使用 malloc( ), deallocate() 直接 使用 free()。 2. 模擬 C++ 的 set_new_handler( ) 處 理記憶 體不足 的狀況 SGI STL 第㆒ 級配置 器 template class __malloc_alloc_template { … }; typedef __malloc_alloc_template<0> malloc_alloc; 其㆗ : 1. allocate( ) 直 接使用 malloc( ), deallocate() 直接 使用 free()。 2. 模擬 C++ 的 set_new_handler( ) 處 理記憶 體不足 的狀況 SGI STL 第㆓級 配置器第㆓級 配置器第㆓級 配置器第㆓級 配置器 template class __default_alloc_template { … }; typedef __default_alloc_template alloc; 其㆗ : 1. 維護16個 free lists, 負責16種 小型區 塊(8,16,24,32...128)的 次配置 能力。 記 憶池(memory pool)乃以 malloc( ) 實 際配置 而得。 如 果記憶 體不足 ,轉呼 叫第㆒ 級配置 器 ( 那兒有 「記憶 體不足 」處理 程序) 。 2. 如 果需求 區塊大 於 128bytes,轉 呼叫 第 ㆒級配 置器。 SGI STL 第㆓級 配置器第㆓級 配置器第㆓級 配置器第㆓級 配置器 template class __default_alloc_template { … }; typedef __default_alloc_template alloc; 其㆗ : 1. 維護16個 free lists, 負責16種 小型區 塊(8,16,24,32...128)的 次配置 能力。 記 憶池(memory pool)乃以 malloc( ) 實 際配置 而得。 如 果記憶 體不足,轉呼 叫第㆒ 級配置 器 ( 那兒有 「記憶 體不足 」處理 程序) 。 2. 如 果需求 區塊大 於 128bytes,轉 呼叫 第 ㆒級配 置器。 http://jjhou.csdn.net jjhou.928@gmail.com 38 pooled allocator in SGI STL 64bytes #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 32bytes 32bytes 32bytes 32bytes 32bytes memory pool free_list[16] start_free end_free 64bytes 64bytes 負責 72bytes 區塊 0 0 96bytes 負責 96bytes 區塊 96bytes 96bytes 0 第㆒ 塊傳 回給 客端 第㆒ 塊傳 回給 客端 第㆒ 塊傳 回給 客戶 這些 連續區 塊,以 union obj 串接 起來, 形成 free list 的實 質組 成。圖 ㆗的小 小箭頭 即 表示 它們形 成㆒個 linked list。 Ref. TASS chap2 SGI STL [提供了 ㆒個 alloc class,透 過它, 可經由 以㆖ memory pool 機制 取得 no-cookie blocks。例 void* p = alloc::allocate(34); 將得 40 bytes. http://jjhou.csdn.net jjhou.928@gmail.com 39 implementation skill for free-list free-list :已 給客戶 這是 ㆒個小 額區塊 (for 小型 物件) Ref. TASS chap2 union obj { union obj* free_list_link; char client_data[1]; // client sees this }; union obj { union obj* free_list_link; char client_data[1]; // client sees this }; 當客 戶端獲 得小額 區塊, 獲得 的即 是 char*( 指向某 個 obj)。 此時 雖然客 戶端沒 有諸如 LString 或 ZString之類的 資訊, 可以 得知區 塊大小 ,但由 於這 區塊 是給小 型物件 所用, 物件 的 ctor 自 然不會 逾越分 寸(根 據 object model)。 "串起free_list_link" 的初始 動作出 現在《STL源碼剖 析》, p66 :被 管理㆗ 改用 struct 可 http://jjhou.csdn.net jjhou.928@gmail.com 40 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.1 //說明:㆒開始什麼都是0 0x0 0x0 poolSize:0 heapSize: 0 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x0 0 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:㆒開始什麼都是0 0x0 0x0 poolSize:0 heapSize: 0 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x0 0 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 Ref. \tass\prog\2alloc-dissect.cpp #283 template #284 __default_alloc_template::obj* volatile #285 __default_alloc_template::free_list[__NFREELISTS] #286 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #287 //---------------------------------------------- #288 #289 //令第2級配置器的名稱為 alloc #290 typedef __default_alloc_template alloc; #283 template #284 __default_alloc_template::obj* volatile #285 __default_alloc_template::free_list[__NFREELISTS] #286 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #287 //---------------------------------------------- #288 #289 //令第2級配置器的名稱為 alloc #290 typedef __default_alloc_template alloc; http://jjhou.csdn.net jjhou.928@gmail.com 41 檢討檢討檢討檢討:先前的設計是,為每個 class 做出 operator new 和 operator delete,其內動作大同 小異。後來改善為令這些 operator new 和 operator delete 呼叫 class static member 'pool' 的 allocate()和 deallocate(),把 責任切割給 'pool'。現在則是 釜底抽薪㆞重載 ::operator new 和 ::operator new,於是 new/delete 任何 classes ㆒定喚 起這個新版本,而讓新版本呼 叫 'pool' 的 allocate() 和 deallocate() //說明:以㆘配置32,pool獲得32*20*2+RoundUp(0>>4)=1280的挹注 //其㆗19個區塊給list#3,1個給客戶,餘640備用。 select (0~15, -1 to end): 3 0x25c1918 0x25c1b98 poolSize:640 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置32,pool獲得32*20*2+RoundUp(0>>4)=1280的挹注 //其㆗19個區塊給list#3,1個給客戶,餘640備用。 select (0~15, -1 to end): 3 0x25c1918 0x25c1b98 poolSize:640 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x0 0 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.2 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 640 start_free end_free void* operator new(size_t size) { return alloc::allocate(size); } void operator delete(void* p, size_t size) { alloc::deallocate(p, size); } class C3 { char m_data[32]; }; new C3; 以㆖ 是兩個 global functions 19個 http://jjhou.csdn.net jjhou.928@gmail.com 42 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.3 //說明:以㆘配置64,取pool劃分為640/64=10個區塊, //1個給客戶,9個掛於list#7。 select (0~15, -1 to end): 7 0x25c1b98 0x25c1b98 poolSize:0 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置64,取pool劃分為640/64=10個區塊, //1個給客戶,9個掛於list#7。 select (0~15, -1 to end): 7 0x25c1b98 0x25c1b98 poolSize:0 heapSize: 1280 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x0 0 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 0 class C7 { char m_data[64]; }; new C7; 9個 http://jjhou.csdn.net jjhou.928@gmail.com 43 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.4 //說明:以㆘配置96,pool獲得96*20*2+RoundUp(1280>>4)挹注 //19個區塊給list#11,1個給客戶,餘2000備用。 select (0~15, -1 to end): 11 0x25c2320 0x25c2af0 poolSize:2000 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置96,pool獲得96*20*2+RoundUp(1280>>4)挹注 //19個區塊給list#11,1個給客戶,餘2000備用。 select (0~15, -1 to end): 11 0x25c2320 0x25c2af0 poolSize:2000 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x0 0 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 2000 19個 http://jjhou.csdn.net jjhou.928@gmail.com 44 SGI memory pool 運行實證運行實證運行實證運行實證.5 //說明:以㆘配置88,從pool取20個區塊大小,1個給客戶, //19個掛於list#10。Pool剩餘2000-88*20=240. select (0~15, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2378 19 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置88,從pool取20個區塊大小,1個給客戶, //19個掛於list#10。Pool剩餘2000-88*20=240. select (0~15, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2378 19 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 240 19個 http://jjhou.csdn.net jjhou.928@gmail.com 45 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.6 //說明:以㆘連續㆔次配置88。直接由#10 list取出給客戶。 //連續㆔次後,得以㆘結果。 select (0~15, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘連續㆔次配置88。直接由#10 list取出給客戶。 //連續㆔次後,得以㆘結果。 select (0~15, -1 to end): 10 0x25c2a00 0x25c2af0 poolSize:240 heapSize: 5200 #0 (8) 0x0 0 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 240 16個 http://jjhou.csdn.net jjhou.928@gmail.com 46 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.7 //說明:以㆘配置8,從pool取20個區塊大小,1個給客戶, //19個掛於list#0。Pool剩餘240-8*20=80. select (0~15, -1 to end): 0 0x25c2aa0 0x25c2af0 poolSize:80 heapSize: 5200 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置8,從pool取20個區塊大小,1個給客戶, //19個掛於list#0。Pool剩餘240-8*20=80. select (0~15, -1 to end): 0 0x25c2aa0 0x25c2af0 poolSize:80 heapSize: 5200 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x0 0 #13(112) 0x0 0 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 80 19個 http://jjhou.csdn.net jjhou.928@gmail.com 47 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.8 //說明:以㆘配置104,list#12之㆗無可用區塊,pool餘量又 //不足以供應1個,於是先將pool餘額撥給list#9,然後獲得 //104*20*2+RoundUp(5200>>4)的挹注,19個給list#12,1個給客戶,餘2408備用。 select (0~15, -1 to end): 12 0x25c3318 0x25c3c80 poolSize:2408 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x0 0 #14 (120) 0x0 0 #15 (128) 0x0 0 //說明:以㆘配置104,list#12之㆗無可用區塊,pool餘量又 //不足以供應1個,於是先將pool餘額撥給list#9,然後獲得 //104*20*2+RoundUp(5200>>4)的挹注,19個給list#12,1個給客戶,餘2408備用。 select (0~15, -1 to end): 12 0x25c3318 0x25c3c80 poolSize:2408 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x0 0 #14 (120) 0x0 0 #15 (128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 2408 19個 http://jjhou.csdn.net jjhou.928@gmail.com 48 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.9 //說明:以㆘配置112,從pool取20個區塊大小,撥1個給客戶, //留19個掛於list#13。Pool剩餘2408-112*20=168. select (0~15, -1 to end): 13 0x25c3bd8 0x25c3c80 poolSize:168 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x25c3388 19 #14 (120) 0x0 0 #15 (128) 0x0 0 //說明:以㆘配置112,從pool取20個區塊大小,撥1個給客戶, //留19個掛於list#13。Pool剩餘2408-112*20=168. select (0~15, -1 to end): 13 0x25c3bd8 0x25c3c80 poolSize:168 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x0 0 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10 (88) 0x25c2480 16 #11 (96) 0x25c1c00 19 #12 (104) 0x25c2b60 19 #13 (112) 0x25c3388 19 #14 (120) 0x0 0 #15 (128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 168 19個 http://jjhou.csdn.net jjhou.928@gmail.com 49 //說明:以㆘配置48,從pool取得3個區塊,1個 //給客戶,2個掛於list#5。Pool剩餘168-48*3=24. select (0~15, -1 to end): 5 0x25c3c68 0x25c3c80 poolSize:24 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置48,從pool取得3個區塊,1個 //給客戶,2個掛於list#5。Pool剩餘168-48*3=24. select (0~15, -1 to end): 5 0x25c3c68 0x25c3c80 poolSize:24 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x0 0 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x25c2aa0 1 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.10 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 24 http://jjhou.csdn.net jjhou.928@gmail.com 50 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.11 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應1個,於是先將pool餘額 //撥給list#2,然後爭取72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小 //(我將它設定為10000),因此記憶體不足,於是反求諸己取80(#9)回填pool,再以之當做72給客戶,餘8備用 select (0~15, -1 to end): 8 0x25c2ae8 0x25c2af0 poolSize:8 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應1個,於是先將pool餘額 //撥給list#2,然後爭取72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小 //(我將它設定為10000),因此記憶體不足,於是反求諸己取80(#9)回填pool,再以之當做72給客戶,餘8備用 select (0~15, -1 to end): 8 0x25c2ae8 0x25c2af0 poolSize:8 heapSize: 9688 #0 (8) 0x25c2a08 19 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c2480 16 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 8 http://jjhou.csdn.net jjhou.928@gmail.com 51 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.12 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應1個,於是先將pool餘額撥給list#0,然後爭取 //72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小(我將它設定為10000), //因此記憶體不足,於是反求諸己取88(#10)回填pool,再以之當做72給客戶,餘16備用。 select (0~15, -1 to end): 8 0x25c24c8 0x25c24d8 poolSize:16 heapSize: 9688 #0 (8) 0x25c2ae8 20 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c24d8 15 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 //說明:以㆘配置72,list#8㆗無可用區塊,pool餘量又不足供應1個,於是先將pool餘額撥給list#0,然後爭取 //72*20*2+RoundUp(9688>>4)的挹注,但此要求已超越system heap大小(我將它設定為10000), //因此記憶體不足,於是反求諸己取88(#10)回填pool,再以之當做72給客戶,餘16備用。 select (0~15, -1 to end): 8 0x25c24c8 0x25c24d8 poolSize:16 heapSize: 9688 #0 (8) 0x25c2ae8 20 #1 (16) 0x0 0 #2 (24) 0x25c3c68 1 #3 (32) 0x25c16b8 19 #4 (40) 0x0 0 #5 (48) 0x25c3c08 2 #6 (56) 0x0 0 #7 (64) 0x25c1958 9 #8 (72) 0x0 0 #9 (80) 0x0 0 #10(88) 0x25c24d8 15 #11(96) 0x25c1c00 19 #12(104) 0x25c2b60 19 #13(112) 0x25c3388 19 #14(120) 0x0 0 #15(128) 0x0 0 #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 16 15個 http://jjhou.csdn.net jjhou.928@gmail.com 52 SGI memory pool 運行 實證運行 實證運行 實證運行 實證.13 //說明:以㆘配置120,list#14㆗無可用區塊,pool餘量又不足供應1個, //於是先將pool餘額撥給list#1,然後爭取120*20*2+RoundUp(9688>>4) //的挹注,但此要求已超越system heap大小(我將它設定為10000), //因此記憶體不足,反求諸己後仍得不到自由區塊,於是失敗。 //檢討:此時其實尚有可用記憶體,包括system heap還有10000-9688=312, //各個free lists內亦可能有些連續自由區塊。 select (0~15, -1 to end): 14 out-of-memory -- jjhou simulation. //說明:以㆘配置120,list#14㆗無可用區塊,pool餘量又不足供應1個, //於是先將pool餘額撥給list#1,然後爭取120*20*2+RoundUp(9688>>4) //的挹注,但此要求已超越system heap大小(我將它設定為10000), //因此記憶體不足,反求諸己後仍得不到自由區塊,於是失敗。 //檢討:此時其實尚有可用記憶體,包括system heap還有10000-9688=312, //各個free lists內亦可能有些連續自由區塊。 select (0~15, -1 to end): 14 out-of-memory -- jjhou simulation. #0 #1 #2 #3 #4 #5 #6 #7 #8 #9 #10 #11 #12 #13 #14 #15 16 http://jjhou.csdn.net jjhou.928@gmail.com 53 SGI's allocator 實作細節實作細節實作細節實作細節, 第㆒級配置器第㆒級配置器第㆒級配置器第㆒級配置器 #008 // 第㆒級配置器 #010 template #011 class __malloc_alloc_template { #012 private: #013 static void* oom_malloc(size_t); #014 static void* oom_realloc(void*, size_t); #015 static void (*__malloc_alloc_oom_handler)(); #016 //㆖面這個指標用來指向new-handler(if any) #017 public: #018 static void* allocate(size_t n) #019 { #020 void *result = malloc(n); //直接使用 malloc() #021 if (0 == result) result = oom_malloc(n); #022 return result; #023 } #024 static void deallocate(void *p, size_t /* n */) #025 { #026 free(p); //直接使用 free() #027 } #028 static void* reallocate(void *p, size_t /* old_sz */, size_t new_sz) #029 { #030 void * result = realloc(p, new_sz); //直接使用 realloc() #031 if (0 == result) result = oom_realloc(p, new_sz); #032 return result; #033 } #034 static void (*set_malloc_handler(void (*f)()))() #035 { //類似 C++ 的 set_new_handler(). #036 void (*old)() = __malloc_alloc_oom_handler; //記錄原new-handler #037 __malloc_alloc_oom_handler = f; //把 f 記起來以便爾後呼叫 #038 return(old); //把原先的handler傳回以便日後可以恢復 #039 } #040 }; #008 // 第㆒級配置器 #010 template #011 class __malloc_alloc_template { #012 private: #013 static void* oom_malloc(size_t); #014 static void* oom_realloc(void*, size_t); #015 static void (*__malloc_alloc_oom_handler)(); #016 //㆖面這個指標用來指向new-handler(if any) #017 public: #018 static void* allocate(size_t n) #019 { #020 void *result = malloc(n); //直接使用 malloc() #021 if (0 == result) result = oom_malloc(n); #022 return result; #023 } #024 static void deallocate(void *p, size_t /* n */) #025 { #026 free(p); //直接使用 free() #027 } #028 static void* reallocate(void *p, size_t /* old_sz */, size_t new_sz) #029 { #030 void * result = realloc(p, new_sz); //直接使用 realloc() #031 if (0 == result) result = oom_realloc(p, new_sz); #032 return result; #033 } #034 static void (*set_malloc_handler(void (*f)()))() #035 { //類似 C++ 的 set_new_handler(). #036 void (*old)() = __malloc_alloc_oom_handler; //記錄原new-handler #037 __malloc_alloc_oom_handler = f; //把 f 記起來以便爾後呼叫 #038 return(old); //把原先的handler傳回以便日後可以恢復 #039 } #040 }; #001 //#include #002 #include #003 #include #004 #005 #define __THROW_BAD_ALLOC \ cerr << "out of memory"; exit(1) #001 //#include #002 #include #003 #include #004 #005 #define __THROW_BAD_ALLOC \ cerr << "out of memory"; exit(1) 連續程式碼,取材自 SGI STL 2.91 ,可通過VC6, CB5 (\handout\alloc.h) 如果 改用 operator new(),便可 直接 使用 C++ 的 set_new_handler(),不 必如 此這般 模擬。 my handler void (*)() typedef void (*H)(); H set_malloc_handler(H f);相當於 從這 裡開始 http://jjhou.csdn.net jjhou.928@gmail.com 54 SGI's allocator 實作細節實作細節實作細節實作細節, 第㆒級配置器第㆒級配置器第㆒級配置器第㆒級配置器, oom-handler #042 template #043 void (*__malloc_alloc_template::__malloc_alloc_oom_handler)() = 0; #044 #045 template //本例 template 參數 'inst' 完全沒派㆖用場 #046 void* __malloc_alloc_template::oom_malloc(size_t n) #047 { #048 void (*my_malloc_handler)(); #049 void* result; #050 #051 for (;;) { //不斷嘗試釋放、配置、再釋放、再配置… #052 my_malloc_handler = __malloc_alloc_oom_handler; //換個名稱(沒啥意義) #053 if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; } #054 (*my_malloc_handler)(); //呼叫處理常式,企圖釋放記憶體 #055 result = malloc(n); //再次嘗試配置記憶體 #056 if (result) return(result); #057 } #058 } #042 template #043 void (*__malloc_alloc_template::__malloc_alloc_oom_handler)() = 0; #044 #045 template //本例 template 參數 'inst' 完全沒派㆖用場 #046 void* __malloc_alloc_template::oom_malloc(size_t n) #047 { #048 void (*my_malloc_handler)(); #049 void* result; #050 #051 for (;;) { //不斷嘗試釋放、配置、再釋放、再配置… #052 my_malloc_handler = __malloc_alloc_oom_handler; //換個名稱(沒啥意義) #053 if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; } #054 (*my_malloc_handler)(); //呼叫處理常式,企圖釋放記憶體 #055 result = malloc(n); //再次嘗試配置記憶體 #056 if (result) return(result); #057 } #058 } #060 template #061 void * __malloc_alloc_template::oom_realloc(void* p, size_t n) #062 { #063 void (*my_malloc_handler)(); #064 void* result; #065 #066 for (;;) { //不斷嘗試釋放、配置、再釋放、再配置… #067 my_malloc_handler = __malloc_alloc_oom_handler; //換個名稱(沒啥意義) #068 if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; } #069 (*my_malloc_handler)(); //呼叫處理常式,企圖釋放記憶體。 #070 result = realloc(p, n); //再次嘗試配置記憶體。 #071 if (result) return(result); #072 } #073 } #060 template #061 void * __malloc_alloc_template::oom_realloc(void* p, size_t n) #062 { #063 void (*my_malloc_handler)(); #064 void* result; #065 #066 for (;;) { //不斷嘗試釋放、配置、再釋放、再配置… #067 my_malloc_handler = __malloc_alloc_oom_handler; //換個名稱(沒啥意義) #068 if (0 == my_malloc_handler) { __THROW_BAD_ALLOC; } #069 (*my_malloc_handler)(); //呼叫處理常式,企圖釋放記憶體。 #070 result = realloc(p, n); //再次嘗試配置記憶體。 #071 if (result) return(result); #072 } #073 } my handler 有可 能在#037被改 掉 關於 oom_handler 的各項思考、警告、 細節…,參考 VC new-handler 投影片 http://jjhou.csdn.net jjhou.928@gmail.com 55 SGI's allocator 實作細節實作細節實作細節實作細節, 另㆒個配置工具另㆒個配置工具另㆒個配置工具另㆒個配置工具(㆒層 薄包裝 ) #074 //---------------------------------------------- #075 typedef __malloc_alloc_template<0> malloc_alloc; #076 #077 //㆒個配置工具 #078 template #079 class simple_alloc { #080 public: #081 static T* allocate(size_t n) //㆒次配置 n 個 T objects #082 { return 0 == n? 0 : (T*)Alloc::allocate(n*sizeof(T)); } #083 static T* allocate(void) //㆒次配置 1 個 T objects #084 { return (T*)Alloc::allocate(sizeof(T)); } #085 static void deallocate(T* p, size_t n) //㆒次歸還 n 個 T objects #086 { if (0 != n) Alloc::deallocate(p, n*sizeof(T)); } #087 static void deallocate(T *p) //㆒次歸還 1 個 T objects #088 { Alloc::deallocate(p, sizeof(T)); } #089 }; #074 //---------------------------------------------- #075 typedef __malloc_alloc_template<0> malloc_alloc; #076 #077 //㆒個配置工具 #078 template #079 class simple_alloc { #080 public: #081 static T* allocate(size_t n) //㆒次配置 n 個 T objects #082 { return 0 == n? 0 : (T*)Alloc::allocate(n*sizeof(T)); } #083 static T* allocate(void) //㆒次配置 1 個 T objects #084 { return (T*)Alloc::allocate(sizeof(T)); } #085 static void deallocate(T* p, size_t n) //㆒次歸還 n 個 T objects #086 { if (0 != n) Alloc::deallocate(p, n*sizeof(T)); } #087 static void deallocate(T *p) //㆒次歸還 1 個 T objects #088 { Alloc::deallocate(p, sizeof(T)); } #089 }; template class vector { protected: // 專屬之空間配置器,每次配置㆒個元素大小 typedef simple_alloc data_allocator; result = data_allocator::allocate(); // 配置1個元素空間 ... data_allocator::deallocate(result); template class vector { protected: // 專屬之空間配置器,每次配置㆒個元素大小 typedef simple_alloc data_allocator; result = data_allocator::allocate(); // 配置1個元素空間 ... data_allocator::deallocate(result); 用例: 容器 對 alloc 的更 多使 用情況 見後頁 http://jjhou.csdn.net jjhou.928@gmail.com 56 SGI's allocator 實作細節實作細節實作細節實作細節, 第㆓級配置器第㆓級配置器第㆓級配置器第㆓級配置器, 資料結構資料結構資料結構資料結構 #098 //本例兩個 template 參數完全沒派㆖用場 #099 template #100 class __default_alloc_template { #101 private: #102 //實際㆖應使用 static const int x = N #103 //取代 #094~#096 的 enum { x = N }, 但目前支援該性質的編譯器不多 #104 #105 static size_t ROUND_UP(size_t bytes) { #106 return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1)); #107 } #108 #109 private: #110 union obj { //type definition #111 union obj* free_list_link; #112 }; //改用 struct 亦可 #113 #114 private: #115 static obj* volatile free_list[__NFREELISTS]; #116 static size_t FREELIST_INDEX(size_t bytes) { #117 return (((bytes) + __ALIGN-1)/__ALIGN - 1); #118 } #119 #120 // Returns an object of size n, and optionally adds to size n free list. #121 static void *refill(size_t n); #122 #123 // Allocates a chunk for nobjs of size "size". nobjs may be reduced #124 // if it is inconvenient to allocate the requested number. #125 static char* chunk_alloc(size_t size, int &nobjs); #126 #127 // Chunk allocation state. #128 static char* start_free; //指向'pool'的頭 #129 static char* end_free; //指向'pool'的尾 #130 static size_t heap_size; //配置總量 #098 //本例兩個 template 參數完全沒派㆖用場 #099 template #100 class __default_alloc_template { #101 private: #102 //實際㆖應使用 static const int x = N #103 //取代 #094~#096 的 enum { x = N }, 但目前支援該性質的編譯器不多 #104 #105 static size_t ROUND_UP(size_t bytes) { #106 return (((bytes) + __ALIGN-1) & ~(__ALIGN - 1)); #107 } #108 #109 private: #110 union obj { //type definition #111 union obj* free_list_link; #112 }; //改用 struct 亦可 #113 #114 private: #115 static obj* volatile free_list[__NFREELISTS]; #116 static size_t FREELIST_INDEX(size_t bytes) { #117 return (((bytes) + __ALIGN-1)/__ALIGN - 1); #118 } #119 #120 // Returns an object of size n, and optionally adds to size n free list. #121 static void *refill(size_t n); #122 #123 // Allocates a chunk for nobjs of size "size". nobjs may be reduced #124 // if it is inconvenient to allocate the requested number. #125 static char* chunk_alloc(size_t size, int &nobjs); #126 #127 // Chunk allocation state. #128 static char* start_free; //指向'pool'的頭 #129 static char* end_free; //指向'pool'的尾 #130 static size_t heap_size; //配置總量 #090 //---------------------------------------------- #091 //第㆓級配置器 #092 //---------------------------------------------- #094 enum {__ALIGN = 8}; //小區塊的㆖調邊界 #095 enum {__MAX_BYTES = 128}; //小區塊的㆖限 #096 enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; //free-list數 #090 //---------------------------------------------- #091 //第㆓級配置器 #092 //---------------------------------------------- #094 enum {__ALIGN = 8}; //小區塊的㆖調邊界 #095 enum {__MAX_BYTES = 128}; //小區塊的㆖限 #096 enum {__NFREELISTS = __MAX_BYTES/__ALIGN}; //free-list數 若bytes為8,則(8+7)/8-1 = 0 若bytes為16,則(16+7)/8-1 = 1 若bytes為20,則(20+7)/8-1 = 2 若bytes為24,則(24+7)/8-1 = 2 若bytes為13,則(13+7) & ~(7) 即 10100 & 11000,得 10000即 16 pool http://jjhou.csdn.net jjhou.928@gmail.com 57 如果 這 p 並 非當初 從此系 統㆗取 得, 仍可 併入這 系統內 (這並 不好) 如果 p 所指 大小不 是 8 倍數 , 交還 此系統 甚至會 帶來災 難 SGI's allocator 實作細節實作細節實作細節實作細節, 第㆓級配置器第㆓級配置器第㆓級配置器第㆓級配置器, allocate/deallocate#132 public: #133 #134 static void* allocate(size_t n) //n must be > 0 #135 { #136 obj* volatile *my_free_list; //obj** #137 obj* result; #138 #139 if (n > (size_t)__MAX_BYTES) { //改用第㆒級配置器 #140 return(malloc_alloc::allocate(n)); #141 } #142 #143 my_free_list = free_list + FREELIST_INDEX(n); #144 result = *my_free_list; #145 if (result == 0) { //list為空 #146 void* r = refill(ROUND_UP(n)); //挹注之 #147 return r; #148 } //若往㆘進行表示list內已有可用區塊 #149 *my_free_list = #150 result->free_list_link; #151 return (result); #152 } #132 public: #133 #134 static void* allocate(size_t n) //n must be > 0 #135 { #136 obj* volatile *my_free_list; //obj** #137 obj* result; #138 #139 if (n > (size_t)__MAX_BYTES) { //改用第㆒級配置器 #140 return(malloc_alloc::allocate(n)); #141 } #142 #143 my_free_list = free_list + FREELIST_INDEX(n); #144 result = *my_free_list; #145 if (result == 0) { //list為空 #146 void* r = refill(ROUND_UP(n)); //挹注之 #147 return r; #148 } //若往㆘進行表示list內已有可用區塊 #149 *my_free_list = #150 result->free_list_link; #151 return (result); #152 } #154 static void deallocate(void* p, size_t n) //p 不為 0 #155 { #156 obj* q = (obj*)p; #157 obj* volatile *my_free_list; //obj** #158 #159 if (n > (size_t) __MAX_BYTES) { #160 malloc_alloc::deallocate(p, n); //改用第㆒級配置器 #161 return; #162 } #163 my_free_list = free_list + FREELIST_INDEX(n); #164 q->free_list_link = *my_free_list; #165 *my_free_list = q; #166 } #167 #168 static void * reallocate(void* p, size_t old_sz, #169 size_t new_sz); //此處略列 #170 }; #154 static void deallocate(void* p, size_t n) //p 不為 0 #155 { #156 obj* q = (obj*)p; #157 obj* volatile *my_free_list; //obj** #158 #159 if (n > (size_t) __MAX_BYTES) { #160 malloc_alloc::deallocate(p, n); //改用第㆒級配置器 #161 return; #162 } #163 my_free_list = free_list + FREELIST_INDEX(n); #164 q->free_list_link = *my_free_list; #165 *my_free_list = q; #166 } #167 #168 static void * reallocate(void* p, size_t old_sz, #169 size_t new_sz); //此處略列 #170 }; free_list_link refill() 會充 填 free list 並 傳回 ㆒個( 其實就 是第 ㆒個 )區塊 的起始 位址 http://jjhou.csdn.net jjhou.928@gmail.com 58 SGI's allocator 實作細節實作細節實作細節實作細節, 第㆓級配置器第㆓級配置器第㆓級配置器第㆓級配置器, chunk_alloc #172 // ---------------------------------------------- #173 // We allocate memory in large chunks in order to avoid fragmenting the #174 // malloc heap too much. We assume that size is properly aligned. #175 // We hold the allocation lock. #176 //---------------------------------------------- #177 template #178 char* #179 __default_alloc_template:: #180 chunk_alloc(size_t size, int& nobjs) #181 { #182 char* result; #183 size_t total_bytes = size * nobjs; #184 size_t bytes_left = end_free - start_free; #185 #186 if (bytes_left >= total_bytes) { //pool空間足以滿足所有需求 #187 result = start_free; #188 start_free += total_bytes; #189 return(result); #190 } else if (bytes_left >= size) { //pool空間只足以滿足㆒個(含)以㆖區塊需求 #191 nobjs = bytes_left / size; //改變需求區塊數(注意 nobjs 是 pass-by-reference) #192 total_bytes = size * nobjs; //改變需求總量(bytes) #193 result = start_free; #194 start_free += total_bytes; #195 return(result); #196 } else { //pool空間不足以滿足㆒個區塊需求 #197 size_t bytes_to_get = //現在打算從 system free-store 取這麼多來挹注 #198 2 * total_bytes + ROUND_UP(heap_size >> 4); #199 //首先嘗試將 pool 做充份運用 #200 if (bytes_left > 0) { //如果 pool 還有空間, #201 obj* volatile *my_free_list = //找出應移轉至第#號free-list(區塊儘可能大) #202 free_list + FREELIST_INDEX(bytes_left); #203 //將pool空間編入第 # 號 free-list(肯定只成1區塊) #204 ((obj*)start_free)->free_list_link = *my_free_list; #205 *my_free_list = (obj*)start_free; #206 } #172 // ---------------------------------------------- #173 // We allocate memory in large chunks in order to avoid fragmenting the #174 // malloc heap too much. We assume that size is properly aligned. #175 // We hold the allocation lock. #176 //---------------------------------------------- #177 template #178 char* #179 __default_alloc_template:: #180 chunk_alloc(size_t size, int& nobjs) #181 { #182 char* result; #183 size_t total_bytes = size * nobjs; #184 size_t bytes_left = end_free - start_free; #185 #186 if (bytes_left >= total_bytes) { //pool空間足以滿足所有需求 #187 result = start_free; #188 start_free += total_bytes; #189 return(result); #190 } else if (bytes_left >= size) { //pool空間只足以滿足㆒個(含)以㆖區塊需求 #191 nobjs = bytes_left / size; //改變需求區塊數(注意 nobjs 是 pass-by-reference) #192 total_bytes = size * nobjs; //改變需求總量(bytes) #193 result = start_free; #194 start_free += total_bytes; #195 return(result); #196 } else { //pool空間不足以滿足㆒個區塊需求 #197 size_t bytes_to_get = //現在打算從 system free-store 取這麼多來挹注 #198 2 * total_bytes + ROUND_UP(heap_size >> 4); #199 //首先嘗試將 pool 做充份運用 #200 if (bytes_left > 0) { //如果 pool 還有空間, #201 obj* volatile *my_free_list = //找出應移轉至第#號free-list(區塊儘可能大) #202 free_list + FREELIST_INDEX(bytes_left); #203 //將pool空間編入第 # 號 free-list(肯定只成1區塊) #204 ((obj*)start_free)->free_list_link = *my_free_list; #205 *my_free_list = (obj*)start_free; #206 } pool end_free start_free pool end_free start_free 20 blocks start_free http://jjhou.csdn.net jjhou.928@gmail.com 59 SGI's allocator 實作細節實作細節實作細節實作細節, 第㆓級配置器第㆓級配置器第㆓級配置器第㆓級配置器, chunk_alloc續 #207 start_free = (char*)malloc(bytes_to_get); //從 system free-store 取這麼多。 #208 if (0 == start_free) { //失敗!以㆘試從更大的 free-list 找空間 #209 int i; #210 obj* volatile *my_free_list, *p; #211 #212 //Try to make do with what we have. That can't hurt. #213 //We do not try smaller requests, since that tends #214 //to result in disaster on multi-process machines. #215 for (i = size; i <= __MAX_BYTES; i += __ALIGN) { //例 88,96,104,112... #216 my_free_list = free_list + FREELIST_INDEX(i); #217 p = *my_free_list; #218 if (0 != p) { //該更大的 free-list 內尚有可用區塊,以㆘釋出㆒塊(only)給pool #219 *my_free_list = p -> free_list_link; #220 start_free = (char*)p; #221 end_free = start_free + i; #222 return(chunk_alloc(size, nobjs)); //遞迴再試㆒次 #223 //此時的pool㆒定夠供應至少㆒個需求區塊 #224 // 而任何殘餘零頭終將被編入適當的(正確的)free-list ㆗ #225 } #226 } #227 end_free = 0; //至此,表示 memory 已山窮水盡. #228 //改用第㆒級配置器(未縮減需求量),看看 oom-handler 能否盡點力 #229 start_free = (char*)malloc_alloc::allocate(bytes_to_get); #230 //這會導致丟出異常,或記憶體不足的情況得到改善 #231 } #232 //至此,表示已從 system free-store 成功取得很多 memory #233 heap_size += bytes_to_get; //更新總配置量 #234 end_free = start_free + bytes_to_get; //挹注pool #235 return(chunk_alloc(size, nobjs)); //遞迴再試㆒次 #236 } #237 } #207 start_free = (char*)malloc(bytes_to_get); //從 system free-store 取這麼多。 #208 if (0 == start_free) { //失敗!以㆘試從更大的 free-list 找空間 #209 int i; #210 obj* volatile *my_free_list, *p; #211 #212 //Try to make do with what we have. That can't hurt. #213 //We do not try smaller requests, since that tends #214 //to result in disaster on multi-process machines. #215 for (i = size; i <= __MAX_BYTES; i += __ALIGN) { //例 88,96,104,112... #216 my_free_list = free_list + FREELIST_INDEX(i); #217 p = *my_free_list; #218 if (0 != p) { //該更大的 free-list 內尚有可用區塊,以㆘釋出㆒塊(only)給pool #219 *my_free_list = p -> free_list_link; #220 start_free = (char*)p; #221 end_free = start_free + i; #222 return(chunk_alloc(size, nobjs)); //遞迴再試㆒次 #223 //此時的pool㆒定夠供應至少㆒個需求區塊 #224 // 而任何殘餘零頭終將被編入適當的(正確的)free-list ㆗ #225 } #226 } #227 end_free = 0; //至此,表示 memory 已山窮水盡. #228 //改用第㆒級配置器(未縮減需求量),看看 oom-handler 能否盡點力 #229 start_free = (char*)malloc_alloc::allocate(bytes_to_get); #230 //這會導致丟出異常,或記憶體不足的情況得到改善 #231 } #232 //至此,表示已從 system free-store 成功取得很多 memory #233 heap_size += bytes_to_get; //更新總配置量 #234 end_free = start_free + bytes_to_get; //挹注pool #235 return(chunk_alloc(size, nobjs)); //遞迴再試㆒次 #236 } #237 } free_list_link 把free-list內的 目前 第㆒塊 當成pool http://jjhou.csdn.net jjhou.928@gmail.com 60 SGI's allocator 實作細節實作細節實作細節實作細節, 第㆓級配置器第㆓級配置器第㆓級配置器第㆓級配置器, refill #240 // Returns an object of size n, and optionally adds to size n free list. #241 // We assume that n is properly aligned. We hold the allocation lock. #242 //---------------------------------------------- #243 template #244 void* __default_alloc_template:: #245 refill(size_t n) //n 已調整至 8 的倍數 #246 { #247 int nobjs = 20; //預設取 20 個區塊(但不㆒定能夠) #248 char* chunk = chunk_alloc(n,nobjs); //nobjs 是 pass-by-reference #249 obj* volatile *my_free_list; //obj** #250 obj* result; #251 obj* current_obj; #252 obj* next_obj; #253 int i; #254 #240 // Returns an object of size n, and optionally adds to size n free list. #241 // We assume that n is properly aligned. We hold the allocation lock. #242 //---------------------------------------------- #243 template #244 void* __default_alloc_template:: #245 refill(size_t n) //n 已調整至 8 的倍數 #246 { #247 int nobjs = 20; //預設取 20 個區塊(但不㆒定能夠) #248 char* chunk = chunk_alloc(n,nobjs); //nobjs 是 pass-by-reference #249 obj* volatile *my_free_list; //obj** #250 obj* result; #251 obj* current_obj; #252 obj* next_obj; #253 int i; #254 #255 if (1 == nobjs) return(chunk); //實際得1,交給客戶 #256 //以㆘開始將所得區塊掛㆖ free-list #257 my_free_list = free_list + FREELIST_INDEX(n); #258 //在 chunk 內建立 free list #259 result = (obj*)chunk; #260 *my_free_list = next_obj = (obj*)(chunk + n); #261 for (i=1; ; ++i) { #262 current_obj = next_obj; #263 next_obj = (obj*)((char*)next_obj + n); #264 if (nobjs-1 == i) { //最後㆒個 #265 current_obj->free_list_link = 0; #266 break; #267 } else { #268 current_obj->free_list_link = next_obj; #269 } #270 } #271 return(result); #272 } #255 if (1 == nobjs) return(chunk); //實際得1,交給客戶 #256 //以㆘開始將所得區塊掛㆖ free-list #257 my_free_list = free_list + FREELIST_INDEX(n); #258 //在 chunk 內建立 free list #259 result = (obj*)chunk; #260 *my_free_list = next_obj = (obj*)(chunk + n); #261 for (i=1; ; ++i) { #262 current_obj = next_obj; #263 next_obj = (obj*)((char*)next_obj + n); #264 if (nobjs-1 == i) { //最後㆒個 #265 current_obj->free_list_link = 0; #266 break; #267 } else { #268 current_obj->free_list_link = next_obj; #269 } #270 } #271 return(result); #272 } free_list_link 0 n nobjs 個 所謂切割就是把指標所指處轉型為obj,再 取其 next_obj 指標繼續行事,㆒而再㆔... http://jjhou.csdn.net jjhou.928@gmail.com 61 SGI's allocator 實作細節實作細節實作細節實作細節, 第㆓級配置器第㆓級配置器第㆓級配置器第㆓級配置器, static data #273 //---------------------------------------------- #274 template #275 char *__default_alloc_template::start_free = 0; #276 #277 template #278 char *__default_alloc_template::end_free = 0; #279 #280 template #281 size_t __default_alloc_template::heap_size = 0; #282 #283 template #284 __default_alloc_template::obj* volatile #285 __default_alloc_template::free_list[__NFREELISTS] #286 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #287 //---------------------------------------------- #288 #289 //令第2級配置器的名稱為 alloc #290 typedef __default_alloc_template alloc; #273 //---------------------------------------------- #274 template #275 char *__default_alloc_template::start_free = 0; #276 #277 template #278 char *__default_alloc_template::end_free = 0; #279 #280 template #281 size_t __default_alloc_template::heap_size = 0; #282 #283 template #284 __default_alloc_template::obj* volatile #285 __default_alloc_template::free_list[__NFREELISTS] #286 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; #287 //---------------------------------------------- #288 #289 //令第2級配置器的名稱為 alloc #290 typedef __default_alloc_template alloc; http://jjhou.csdn.net jjhou.928@gmail.com 62 SGI STL 容器對容器對容器對容器對 Allocator 的使用的使用的使用的使用, 1 template class vector { protected: typedef simple_alloc data_allocator; protected: iterator allocate_and_fill(size_type n, const T& x) { iterator result = data_allocator::allocate(n); uninitialized_fill_n(result, n, x); return result; } }; template class vector { protected: typedef simple_alloc data_allocator; protected: iterator allocate_and_fill(size_type n, const T& x) { iterator result = data_allocator::allocate(n); uninitialized_fill_n(result, n, x); return result; } }; template class list { protected: typedef __list_node list_node; typedef simple_alloc list_node_allocator; protected: link_type get_node() { return list_node_allocator::allocate(); } void put_node(link_type p) { list_node_allocator::deallocate(p); } }; template class list { protected: typedef __list_node list_node; typedef simple_alloc list_node_allocator; protected: link_type get_node() { return list_node_allocator::allocate(); } void put_node(link_type p) { list_node_allocator::deallocate(p); } }; template class deque { protected: typedef simple_alloc data_allocator; public: void clear() { for (...) { ... data_allocator::deallocate(*node, buffer_size()); } }; template class deque { protected: typedef simple_alloc data_allocator; public: void clear() { for (...) { ... data_allocator::deallocate(*node, buffer_size()); } }; template class simple_alloc { public: static T *allocate(size_t n) { return 0 == n? 0 : (T*) Alloc::allocate(n*sizeof(T)); static T *allocate(void) { return (T*) Alloc::allocate(sizeof(T)); } static void deallocate(T*, size_t ) { if (0 != n) Alloc::deallocate(p, n * sizeof(T)); } static void deallocate(T*) { Alloc::deallocate(p, sizeof(T)); } }; template class simple_alloc { public: static T *allocate(size_t n) { return 0 == n? 0 : (T*) Alloc::allocate(n*sizeof(T));} static T *allocate(void) { return (T*) Alloc::allocate(sizeof(T)); } static void deallocate(T*, size_t ) { if (0 != n) Alloc::deallocate(p, n * sizeof(T)); } static void deallocate(T*) { Alloc::deallocate(p, sizeof(T)); } }; 多㆒ 層包裝 ,將配 置之型 別記錄 於 template 參數 所有 SGI STL 容器內部都使用 這㆒層,而非最底層的 alloc 當 vector 的 記憶體 需求量 愈來愈 大, 便都 轉至第 ㆒級 allocator 去服務 http://jjhou.csdn.net jjhou.928@gmail.com 63 SGI STL 容器對容器對容器對容器對 Allocator 的使用的使用的使用的使用, 2 template class rb_tree { protected: typedef __rb_tree_node rb_tree_node; typedef simple_alloc rb_tree_node_allocator; link_type get_node() { return rb_tree_node_allocator::allocate(); } ... }; template class rb_tree { protected: typedef __rb_tree_node rb_tree_node; typedef simple_alloc rb_tree_node_allocator; link_type get_node() { return rb_tree_node_allocator::allocate(); } ... }; template class hashtable { private: typedef __hashtable_node node; typedef simple_alloc node_allocator; protected: node* new_node(const value_type& obj) { node* n = node_allocator::allocate(); ... } void delete_node(node* n) { ... node_allocator::deallocate(n); } }; template class hashtable { private: typedef __hashtable_node node; typedef simple_alloc node_allocator; protected: node* new_node(const value_type& obj) { node* n = node_allocator::allocate(); ... } void delete_node(node* n) { ... node_allocator::deallocate(n); } }; http://jjhou.csdn.net jjhou.928@gmail.com 64 SGI's allocator 和容器的關 係和容器的關 係和容器的關 係和容器的關 係 list v; //假設 sizeof(Foo) 小於 128 v.push_back( Foo(1) ); //暫時物件,memory來自stack //至此,㆖述暫時物件 Foo(1) 消亡 v.push_back( new Foo(2) ); //錯誤(元素型別不符) Foo* p = new Foo(2); //動態物件,memory來自heap v.push_back( *p ); delete p; //至此,㆖述物件 Foo(2) 消亡 Foo(1) Foo(1) 這㆒ 塊是vector::push_back() 透過 alloc 取得, 不帶cookie STL 容器( 只)支 援 value 語意 所以 這裡引 發copy Foo(2) Foo(1) 這㆒ 塊是vector::push_back() 透過 alloc 取得, 不帶cookie STL 容器( 只)支 援 value 語意 所以 這裡引 發copy cookie Foo(2) 註: 有的vector版本會 ㆒ 開始 就配置 許多元 素空間 http://jjhou.csdn.net jjhou.928@gmail.com 66 implementation skill for free-list SGI STL allocator 如何能夠將㆒個不帶 cookie 的區塊歸還到正確的 free list ㆖頭? // 如果直 接使用 alloc,歸 還時就 必須指 出 block 大小 void* p1 = alloc::allocate(40); alloc::deallocate(p1,40); // 如果直 接使用 alloc,歸 還時就 必須指 出 block 大小 void* p1 = alloc::allocate(40); alloc::deallocate(p1,40); // 改用 simple_alloc,歸還時就不再需要由使用者指定 block size // 以㆘兩者都是 class template ㆗的 static functions double* p2 = simple_alloc::allocate(1); simple_alloc::deallocate(p2); // 改用 simple_alloc,歸還時就不再需要由使用者指定 block size // 以㆘兩者都是 class template ㆗的 static functions double* p2 = simple_alloc::allocate(1); simple_alloc::deallocate(p2); template class simple_alloc { public: static T *allocate(size_t n) { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof(T)); } static T *allocate(void) { return (T*) Alloc::allocate(sizeof(T)); } static void deallocate(T *p, size_t n) { if (0 != n) Alloc::deallocate(p, n * sizeof(T)); } static void deallocate(T *p) { Alloc::deallocate(p, sizeof(T)); } }; template class simple_alloc { public: static T *allocate(size_t n) { return 0 == n? 0 : (T*) Alloc::allocate(n * sizeof(T)); } static T *allocate(void) { return (T*) Alloc::allocate(sizeof(T)); } static void deallocate(T *p, size_t n) { if (0 != n) Alloc::deallocate(p, n * sizeof(T)); } static void deallocate(T *p) { Alloc::deallocate(p, sizeof(T)); } }; n 指的 是區塊 個數, 不是 bytes 所有 SGI STL 容器內 部都使 用 simple_alloc 這㆒ 層,而 非最底 層的 alloc 是嗎 ? 如果定義㆒個專屬配置器也許比較好些: typedef simple_alloc doubleAlloc; double* p3 = doubleAlloc::allocate(1); doubleAlloc::deallocate(p3); 其實這就是 vector 的作法,它定義了 typedef simple_alloc date_allocator; 因此 vector 用的就是 simple http://jjhou.csdn.net jjhou.928@gmail.com 67 small object allocator in Loki Ref. MCD chap4, and Loki smallObj.cpp 4. SmallObject * 提供物件層次(object level)的服務 * 透通性 “ classes 繼承 SmallObject 即可享受服務 3. SmallObjAllocator * 能夠配置多種尺寸不同的小型物件,類似 std::alloc。 * 可根據參數而組態化(configurable) * 內含 vector 2. FixedAllocator * 配置某固定大小的物件,類似㆒個 free-list。 * 內含 vector,因此可以無限擴充(多個) char[blockSize * blocks]; 1. Chunk * 根據某特定大小來配置物件。有物件個數㆖限。 * 類似㆒個元素個數受限的 free-list。 * Init() 時 new char[blockSize * blocks]; 而後呼叫 Reset() 將 每隔 blockSize 個 bytes 的 1st byte 設為流水號碼(1~blocks), 以此做為 index 來管理 blocks. http://jjhou.csdn.net jjhou.928@gmail.com 68 Loki small object allocator, overview f12 f16 f30 f32 f48 ... 負責 30 bytes 區塊 的配 置和歸 還 可由 client 自 由添加 ㆒定 會按區 塊大小 排序 負責 48 bytes 區塊 的配 置和歸 還 blocksAvailable_ firstAvailableBlock_ pData_ 1 16 bytes (blockSize_) m 1 m blocksAvailable_ firstAvailableBlock_ pData_ ... SmallObjAllocator 內含 vector pool_; FixedAllocator 內含 vector Chunk chunkSize_ (bytes) numBlocks (blocks) 在 SGI STL ㆗,每㆒個 fixed size blocks 都被安置於 free list,並以其前 4 bytes 做為 singly linked list 所需的 pointer。在 Loki ㆗,每㆒個 fixed size blocks 被安置於 chunk,那是塊連續空 間,不會變大也不會變小。這裡與 STL's free list 對應的是 vector。 #0 #1 #2 #3 32bytes 32bytes 32bytes 32bytes 32bytes free_list[16] 0 第㆒ 塊傳 回給 客戶 ...2 3 Loki 把index當做ptr來 使用, 在chunk內實 施的動 作(配 置 或歸 還)完 全是標 準 list 動作 。 SGI STLindex backfront ... 只有 當某尺 寸的需 求至少 出現 ㆒次才 產出㆒ 個對應 的 FixedAllocator http://jjhou.csdn.net jjhou.928@gmail.com 69 雖然 要求區 塊的時 間次 序是 :20, 64, 40,代 表 創建 FixedAllocator 的時 間順 序,但 置於 vector 內的 位置次 序卻是 20, 40,64, 以區塊 大小排 序。 侯捷 認為排 序意義 不大. Loki small object allocator, 1, allocate vector for 20 vector for 40 vector for 64 101 1 pData_ 1 40 bytes per block. (40*102 =4080) 102 共 1 個 Chunks SmallObjAllocator myAlloc(2048,256); // 可應 付小於 256 的 各種大 小的配 置 void* p1 = (void*)myAlloc.Allocate(20); void* p4 = (void*)myAlloc.Allocate(64); void* p2 = (void*)myAlloc.Allocate(40); void* p3 = (void*)myAlloc.Allocate(300); // 大於 256,遂 轉交系 統處理 void* p5 = (void*)myAlloc.Allocate(64); void* p6 = (void*)myAlloc.Allocate(64); void* p7 = (void*)myAlloc.Allocate(64); 60 4 pData_ 1 64 bytes per block 64 共 1 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 5 allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ indexindexindex chunk 大小 = 20*204= 4080 chunk size(實際㆖沒被loki程式碼用到,大概是實作㆖的遺 漏吧。程式碼㆗預設使用4096,並總是使用 4096) maxBlock p1 p2 p4,p5,p6,p7 SmallObjAllocator 內含 vector start finish start finish start finish start finish http://jjhou.csdn.net jjhou.928@gmail.com 70 Loki small object allocator, 2, allocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks // 續㆖頁 程式 void* ptr[100]; for (int i=0; i< 65; ++i) ptr[i] = (void*)myAlloc.Allocate(64); 0 64 pData_ 1 64 bytes per block 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 1 64 5 deallocChunk_ allocChunk_ index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ vector for 20 vector for 40 vector for 64 SmallObjAllocator 內含 vector start finish start finish start finish start finish start finish http://jjhou.csdn.net jjhou.928@gmail.com 71 Loki small object allocator, 3, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 1 1 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 1 64 // 續㆖頁 程式 myAlloc.Deallocate(p5,64); // 註,其 ㆗運用deallocChunk_ 協助儘可能 快速㆞ 找到 // 「待刪 區塊落 在哪㆒ 個 chunk 內」 // 可觀察 SmallObj.cpp 的 VicinityFind() p5 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ deallocChunk_ allocChunk_ vector for 20 vector for 40 vector for 64start finish start finish start finish start finish start finish start finish SmallObjAllocator 內含 vector start finish http://jjhou.csdn.net jjhou.928@gmail.com 72 Loki small object allocator, 4, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 2 2 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 1 64 1 4 // 續㆖頁 程式 myAlloc.Deallocate(p6,64); p6 index index index deallocChunk_ allocChunk_ allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ vector for 20 vector for 40 vector for 64 SmallObjAllocator 內含 vector start finish start finish start finish start finish start finish start finish start finish start finish start finish http://jjhou.csdn.net jjhou.928@gmail.com 73 Loki small object allocator, 5, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 3 0 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 2 64 1 4 // 續㆖頁 程式 myAlloc.Deallocate(p4,64); p4 indexindex index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ deallocChunk_ allocChunk_ vector for 20 vector for 40 vector for 64 SmallObjAllocator 內含 vector start finish start finish start finish start finish start finish start finish start finish start finish start finish http://jjhou.csdn.net jjhou.928@gmail.com 74 Loki small object allocator, 6, deallocate 101 1 pData_ 1 40 bytes per block 102 共 1 個 Chunks 4 3 pData_ 1 64 共 2 個 Chunks 203 1 pData_ 1 20 bytes per block 204 共 1 個 Chunks 2 3 4 6 59 5 pData_ 5 64 bytes per block 2 64 1 0 // 續㆖頁 程式 myAlloc.Deallocate(p7,64); p7 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ deallocChunk_ allocChunk_ vector for 20 vector for 40 vector for 64 SmallObjAllocator 內含 vector start finish start finish start finish start finish start finish start finish start finish start finish start finish http://jjhou.csdn.net jjhou.928@gmail.com 75 Loki small object allocator, 7, deallocate 102 0 pData_ 1 40 bytes per block 102 共 1 個 Chunks 64 4 pData_ 5 64 bytes per block 64 共 1 個 Chunks 204 0 pData_ 1 20 bytes per block 204 共 1 個 Chunks 0 1 2 3 // 續㆖ 頁程式 myAlloc.Deallocate(p1,20); myAlloc.Deallocate(p2,40); myAlloc.Deallocate(p3,300); for (int i=0; i< 65; ++i) myAlloc.Deallocate(ptr[i], 64); 真的 釋放了 ㆒個 Chunk 還 給系統 6 7 index index index allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ allocChunk_ deallocChunk_ vector for 20 vector for 40 vector for 64 SmallObjAllocator 內含 vector start finish start finish start finish start finish start finish start finish start finish start finish start finish http://jjhou.csdn.net jjhou.928@gmail.com 76 small object allocator in Loki ㆔㆔㆔㆔ 個主要個主要個主要個主要 classes 的的的的關係關係關係關係 FixedAllocator SmallObjAllocator pool_ : vector pLastAlloc : FixedAllocator* pLastDealloc: FixedAllocator* chunkSize : size_t maxObjectSize : sizt_t chunks_ : vector allocChunk_ : Chunk* deallocChunnk_ : Chunk* Chunk pData_ : unsigned char* firstAvailableBlock_ : unsigned char blocksAvailable_ : unsigned char 1..n 1..n http://jjhou.csdn.net jjhou.928@gmail.com 77 Loki small object allocator, level 1 Ref. MCD chap4, and Loki smallObj.cpp class FixedAllocator { private: struct Chunk { void Init(blockSize,blocks); void* Allocate(_); void Deallocate(_,_); unsigned char* pData_; unsigned char firstAvailableBlock_, blocksAvailable_; }; ... }; class FixedAllocator { private: struct Chunk { void Init(blockSize,blocks); void* Allocate(_); void Deallocate(_,_); unsigned char* pData_; unsigned char firstAvailableBlock_, blocksAvailable_; }; ... }; blocksAvailable_ : 255 firstAvailableBlock_ : 0 pData_ 1 2 254 255 Init(4,255); 4 bytes blocksAvailable_ : 254 firstAvailableBlock_ : 1 pData_ 2 254 255 Allocate(4); 這個 chunk 只負 責供應 4byte block 現在 ,配置 ㆒個 4byte block index index pData_ = new unsigned char [blockSize * blocks]; 這表 示每個chunk內的 區 塊數 最多只 能255 http://jjhou.csdn.net jjhou.928@gmail.com 78 Loki small object allocator, level 1, 實作實作實作實作 Ref. MCD chap4, and Loki smallObj.cpp void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks) { pData_ = new unsigned char[blockSize * blocks]; Reset(blockSize, blocks); } void FixedAllocator::Chunk::Init(std::size_t blockSize, unsigned char blocks) { pData_ = new unsigned char[blockSize * blocks]; Reset(blockSize, blocks); } 01 void FixedAllocator::Chunk::Reset(std::size_t blockSize, 02 unsigned char blocks) 03 { 04 firstAvailableBlock_ = 0; 05 blocksAvailable_ = blocks; 06 07 unsigned char i = 0; 08 unsigned char* p = pData_; 09 for (; i != blocks; p += blockSize) //流水標 示索引 10 *p = ++i; 11 } 01 void FixedAllocator::Chunk::Reset(std::size_t blockSize, 02 unsigned char blocks) 03 { 04 firstAvailableBlock_ = 0; 05 blocksAvailable_ = blocks; 06 07 unsigned char i = 0; 08 unsigned char* p = pData_; 09 for (; i != blocks; p += blockSize) //流水標 示索引 10 *p = ++i; 11 } void FixedAllocator::Chunk::Release() { delete[] pData_; //釋放自 己 } //將被 ㆖㆒層 呼叫 void FixedAllocator::Chunk::Release() { delete[] pData_; //釋放自 己 } //將被 ㆖㆒層 呼叫 01 void* FixedAllocator::Chunk::Allocate(std::size_t blockSiz 02 { 03 if (!blocksAvailable_) return 0; //此 ㆞無銀 04 05 unsigned char* pResult = //指 向第㆒ 個可用 區塊 06 pData_ + (firstAvailableBlock_ * blockSize); 07 08 firstAvailableBlock_ = *pResult; //rhs 視為㆘個可用區 塊的索 09 --blocksAvailable_; 10 11 return pResult; 12 } 01 void* FixedAllocator::Chunk::Allocate(std::size_t blockSiz 02 { 03 if (!blocksAvailable_) return 0; //此 ㆞無銀 04 05 unsigned char* pResult = //指 向第㆒ 個可用 區塊 06 pData_ + (firstAvailableBlock_ * blockSize); 07 08 firstAvailableBlock_ = *pResult; //rhs 視為㆘個可用區 塊的索 引 09 --blocksAvailable_; 10 11 return pResult; 12 }01 void FixedAllocator::Chunk::Deallocate(void* p, 02 std::size_t blockSize) 03 { 04 unsigned char* toRelease = static_cast(p); 05 06 *toRelease = firstAvailableBlock_; 07 firstAvailableBlock_ = static_cast( 08 (toRelease - pData_) / blockSize); 09 10 ++blocksAvailable_; //可 用區塊 數加 1 11 } 01 void FixedAllocator::Chunk::Deallocate(void* p, 02 std::size_t blockSize) 03 { 04 unsigned char* toRelease = static_cast(p); 05 06 *toRelease = firstAvailableBlock_; 07 firstAvailableBlock_ = static_cast( 08 (toRelease - pData_) / blockSize); 09 10 ++blocksAvailable_; //可 用區塊 數加 1 11 } 1 2 3 4 ... ... 254 1 pData_ 1 255 2 配置 第㆒個 區塊 後的 狀態: 253 2 pData_ 1 255 2 配置 第㆓個 區塊 後的 狀態: 254 0 pData_ 2 255 歸還 第㆒個 區塊 後的 狀態: 1 23 http://jjhou.csdn.net jjhou.928@gmail.com 79 01 void* FixedAllocator::Chunk::Allocate(std::size_t blockSize) 02 { 03 if (!blocksAvailable_) return 0; //此 ㆞無銀 04 05 unsigned char* pResult = //指 向第㆒ 個可用 區塊 06 pData_ + (firstAvailableBlock_ * blockSize); 07 08 firstAvailableBlock_ = *pResult; //rhs 被視 為㆘個可用 區塊的 索引 09 --blocksAvailable_; 10 11 return pResult; 12 } 01 void* FixedAllocator::Chunk::Allocate(std::size_t blockSize) 02 { 03 if (!blocksAvailable_) return 0; //此 ㆞無銀 04 05 unsigned char* pResult = //指 向第㆒ 個可用 區塊 06 pData_ + (firstAvailableBlock_ * blockSize); 07 08 firstAvailableBlock_ = *pResult; //rhs 被視 為㆘個可用 區塊的 索引 09 --blocksAvailable_; 10 11 return pResult; 12 } Loki small object allocator, level 1, 實作實作實作實作 Ref. MCD chap4, and Loki smallObj.cpp 64 4 pData_ 5 64 0 1 2 3 6 7 index 63 3 pData_ 5 64 0 1 2 3 6 7 index #09 #08 blockSize blocksAvailable_ firstAvailableBlock_ pResult#05 pResult#011 http://jjhou.csdn.net jjhou.928@gmail.com 80 Loki small object allocator, level 1, 實作實作實作實作 Ref. MCD chap4, and Loki smallObj.cpp 01 void FixedAllocator::Chunk::Deallocate(void* p, 02 std::size_t blockSize) 03 { 04 unsigned char* toRelease = static_cast(p); 05 06 *toRelease = firstAvailableBlock_; 07 firstAvailableBlock_ = static_cast( 08 (toRelease - pData_) / blockSize); 09 ++blocksAvailable_; //可 用區塊 數加 1 10 } 01 void FixedAllocator::Chunk::Deallocate(void* p, 02 std::size_t blockSize) 03 { 04 unsigned char* toRelease = static_cast(p); 05 06 *toRelease = firstAvailableBlock_; 07 firstAvailableBlock_ = static_cast( 08 (toRelease - pData_) / blockSize); 09 ++blocksAvailable_; //可 用區塊 數加 1 10 } 63 3 pData_ 5 64 0 1 2 6 7 index p 64 4 pData_ 5 64 0 1 2 3 6 7 index (toRelease - pData_) / blockSize Î 4 #06 #07 #09 #08 firstAvailableBlock_ blocksAvailable_ toRelease#04 http://jjhou.csdn.net jjhou.928@gmail.com 81 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2 class FixedAllocator { private: std::size_t blockSize_; // n unsigned char numBlocks_; // m typedef std::vector Chunks; Chunks chunks_; Chunk* allocChunk_; //初值是0 Chunk* deallocChunk_; //初值是0 //For ensuring proper copy semantics mutable const FixedAllocator* prev_; mutable const FixedAllocator* next_; ... }; class FixedAllocator { private: std::size_t blockSize_; // n unsigned char numBlocks_; // m typedef std::vector Chunks; Chunks chunks_; Chunk* allocChunk_; //初值是0 Chunk* deallocChunk_; //初值是0 //For ensuring proper copy semantics mutable const FixedAllocator* prev_; mutable const FixedAllocator* next_; ... }; blocksAvailable_ firstAvailableBlock_ pData_ 1 n bytes blocksAvailable_ firstAvailableBlock_ pData_ m 1 m blocksAvailable_ firstAvailableBlock_ pData_ 1 m chunks_front back FixedAllocator f16(16); // 16 表示區塊大小 // 區塊數 m 為以㆘㆔者之㆒: // m=C/n 或 255 或 8*n // 其㆗ C 為 chunk 大小,預設4096 FixedAllocator f16(16); // 16 表示區塊大小 // 區塊數 m 為以㆘㆔者之㆒: // m=C/n 或 255 或 8*n // 其㆗ C 為 chunk 大小,預設4096 ㆒個 FixedAllocator object 功能就像 SGI allocator 的㆒個 free list 似的。其內可有多個 'chunk',每個 'chunk' 管理 m 個 n-bytes 區塊。 有趣 的是,Loki 總是使 用 DEFAULT_CHUNK_SIZE 而 不考 慮SmallObjAllocator::chunkSize_。後 者完全 沒被用 到 (a bug?) 以 cache 和 locality 強化 chunks_ 的搜尋 速度 只用 於 ctor, copy ctor, dtor, Q:為什 麼需要 特別考 慮 copy semantics 呢? 表現 於 allocChunk_ 和 deallocChunk_ 見㆘頁右㆘建構式 http://jjhou.csdn.net jjhou.928@gmail.com 82 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2, 實作實作實作實作 #01 void* FixedAllocator::Allocate() #02 { #03 if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0) #04 { //目前 沒有標 定chunk或該chunk已無可 用區塊 #05 Chunks::iterator i = chunks_.begin(); //從 頭找起 #06 for (;; ++i) #07 { #08 if (i == chunks_.end()) //到 達尾端 ,都沒 找著 #09 { #10 // Initialize #11 chunks_.push_back(Chunk( )); //產生 a new chunk 掛於 末端 #12 Chunk& newChunk = chunks_.back(); //指向 末端 chunk #13 newChunk.Init(blockSize_, numBlocks_); //設好狀 態 #14 allocChunk_ = &newChunk; //標定 ,稍後 將對此 new chunk 取區塊 #15 deallocChunk_ = &chunks_.front(); //另㆒ 標定 #16 break; #17 } #18 if (i->blocksAvailable_ > 0) #19 { //current chunk 有可用 區塊 #20 allocChunk_ = &*i; //取其 位址 #21 break; //不找 了,退 出迴圈 #22 } #23 } #24 } #25 return allocChunk_->Allocate(blockSize_); //向這個chunk 取區 塊 #26 } #01 void* FixedAllocator::Allocate() #02 { #03 if (allocChunk_ == 0 || allocChunk_->blocksAvailable_ == 0) #04 { //目前 沒有標 定chunk或該chunk已無可 用區塊 #05 Chunks::iterator i = chunks_.begin(); //從 頭找起 #06 for (;; ++i) #07 { #08 if (i == chunks_.end()) //到 達尾端 ,都沒 找著 #09 { #10 // Initialize #11 chunks_.push_back(Chunk( )); //產生 a new chunk 掛於 末端 #12 Chunk& newChunk = chunks_.back(); //指向 末端 chunk #13 newChunk.Init(blockSize_, numBlocks_); //設好狀 態 #14 allocChunk_ = &newChunk; //標定 ,稍後 將對此 new chunk 取區塊 #15 deallocChunk_ = &chunks_.front(); //另㆒ 標定 #16 break; #17 } #18 if (i->blocksAvailable_ > 0) #19 { //current chunk 有可用 區塊 #20 allocChunk_ = &*i; //取其 位址 #21 break; //不找 了,退 出迴圈 #22 } #23 } #24 } #25 return allocChunk_->Allocate(blockSize_); //向這個chunk 取區 塊 #26 } //找遍 每個chunk 直至找 到擁有 可用區 塊者. 每在 某chunk找到可 用區塊 ,㆘次 就從該 chunk 找起 找到 適當chunk就記 錄㆘來 , ㆘次 就從那 兒找起 假設 blockSize 是 20,blocks 個數 是 4096 / 20 = 204, 不大 於 256, OK. 假設 blockSize 是 16,blocks 個數 是 4096 / 16 = 256, 不大 於 256, OK FixedAllocator::FixedAllocator(std::size_t blockSize) : blockSize_(blockSize) , deallocChunk_(0) , allocChunk_(0) { prev_ = next_ = this; //以 ㆘計算 並設定 numBlocks_; std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize; if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX; else if (numBlocks == 0) numBlocks = 8 * blockSize; numBlocks_ = static_cast(numBlocks); } FixedAllocator::FixedAllocator(std::size_t blockSize) : blockSize_(blockSize) , deallocChunk_(0) , allocChunk_(0) { prev_ = next_ = this; //以 ㆘計算 並設定 numBlocks_; std::size_t numBlocks = DEFAULT_CHUNK_SIZE / blockSize; if (numBlocks > UCHAR_MAX) numBlocks = UCHAR_MAX; else if (numBlocks == 0) numBlocks = 8 * blockSize; numBlocks_ = static_cast(numBlocks); } 1 2 3 void FixedAllocator::Deallocate(void* p) { deallocChunk_ = VicinityFind(p); DoDeallocate(p); } void FixedAllocator::Deallocate(void* p) { deallocChunk_ = VicinityFind(p); DoDeallocate(p); } (內 部函式, ㆘頁) 執行 deallocation. 假設 deallocChunk_ 已經指 向正確 的 chunk. (內 部函式, ㆘頁) 找 出相對 於某指 標的 那個 chunk, 採用㆒ 種高效 搜尋法. 先對iterator取 值獲得 元素, 再取 該元素 的位址 創建 ㆒暫時 物件拷 貝至容 器然後 結束生 命 ref. VC's 其值 為 0xFF 注意注意注意注意 :只在allocChunk_和deallocChunk_為左值 時才標 示為紫色和綠色 http://jjhou.csdn.net jjhou.928@gmail.com 83 Ref. MCD chap4, and Loki smallObj.cpp Loki small object allocator, level 2, 實作實作實作實作 #01 // FixedAllocator::VicinityFind (internal) #02 // Finds the chunk corresponding to a pointer, using an efficient search #03 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p) #04 { #05 const std::size_t chunkLength = numBlocks_ * blockSize_; #06 #07 Chunk* lo = deallocChunk_; #08 Chunk* hi = deallocChunk_ + 1; #09 Chunk* loBound = &chunks_.front(); #10 Chunk* hiBound = &chunks_.back() + 1; #11 #12 for (;;) { //兵 分兩路 #13 if (lo) { //㆖ 路未到 盡頭 #14 if (p >= lo->pData_ && p < lo->pData_ + chunkLength) #15 return lo; //p指標值 落於 lo 區間 內 #16 if (lo == loBound) lo = 0; //到頂了 ,讓迴 圈結束 #17 else --lo; //否則 往 lo 的 front 繼續 找 #18 } #19 #20 if (hi) { //㆘ 路未到 盡頭 #21 if (p >= hi->pData_ && p < hi->pData_ + chunkLength) #22 return hi; //p指 標值落 於 hi 區間 內 #23 if (++hi == hiBound) hi = 0; //準備往 hi 的 back 繼續 找 #24 } //若 到最尾 則讓迴 圈結束 #25 } #26 return 0; #27 } #01 // FixedAllocator::VicinityFind (internal) #02 // Finds the chunk corresponding to a pointer, using an efficient search #03 FixedAllocator::Chunk* FixedAllocator::VicinityFind(void* p) #04 { #05 const std::size_t chunkLength = numBlocks_ * blockSize_; #06 #07 Chunk* lo = deallocChunk_; #08 Chunk* hi = deallocChunk_ + 1; #09 Chunk* loBound = &chunks_.front(); #10 Chunk* hiBound = &chunks_.back() + 1; #11 #12 for (;;) { //兵 分兩路 #13 if (lo) { //㆖ 路未到 盡頭 #14 if (p >= lo->pData_ && p < lo->pData_ + chunkLength) #15 return lo; //p指標值 落於 lo 區間 內 #16 if (lo == loBound) lo = 0; //到頂了 ,讓迴 圈結束 #17 else --lo; //否則 往 lo 的 front 繼續 找 #18 } #19 #20 if (hi) { //㆘ 路未到 盡頭 #21 if (p >= hi->pData_ && p < hi->pData_ + chunkLength) #22 return hi; //p指 標值落 於 hi 區間 內 #23 if (++hi == hiBound) hi = 0; //準備往 hi 的 back 繼續 找 #24 } //若 到最尾 則讓迴 圈結束 #25 } #26 return 0; #27 } blocksAvailable_ firstAvailableBlock_ pData_ 1 m 2 blocksAvailable_ firstAvailableBlock_ pData_ 1 m 2 如果 找不到 就往 front 找去 如果 找不到 就往 back 找去 front back 如果 這 p 並 非當初 從此系 統㆗取 得,這 裡便肯 定找不 到對應 的 chunk, 於是 永遠不 以 return 跳出 for loop,於 是陷於 無窮迴 路之㆗ 。已證 實。 http://jjhou.csdn.net jjhou.928@gmail.com 84 Ref. MCD chap4, and Loki smallObj.cppLoki small object allocator, level 2, 實作實作實作實作 #0001 void FixedAllocator::DoDeallocate(void* p) #0002 { #0003 //呼叫 chunk's 歸還函式, 將調整 inner list 但未釋放 memory #0004 deallocChunk_->Deallocate(p, blockSize_); #0005 #0006 if (deallocChunk_->blocksAvailable_ == numBlocks_) { #0007 // deallocChunk_ 目前完全 free, 該 release 它嗎? #0008 // 注意,以㆘㆔種情況都重新設定了allocChunk_ #0009 Chunk& lastChunk = chunks_.back(); //標示出最後㆒個 #0010 //如果 "最後㆒個" 就是 "當前回收者" #0011 if (&lastChunk == deallocChunk_) { #0012 // 檢查是否擁有兩個 last chunks empty #0013 // 這只需往 front 方向看前㆒個 chunk 便知. #0014 if (chunks_.size() > 1 && #0015 deallocChunk_[-1].blocksAvailable_ == numBlocks_) { #0016 // 是的,有 2 free chunks, 拋棄最後㆒個 #0017 lastChunk.Release(); #0018 chunks_.pop_back(); #0019 allocChunk_ = deallocChunk_ = &chunks_.front(); #0020 } #0021 return; #0022 } #0023 #0001 void FixedAllocator::DoDeallocate(void* p) #0002 { #0003 //呼叫 chunk's 歸還函式, 將調整 inner list 但未釋放 memory #0004 deallocChunk_->Deallocate(p, blockSize_); #0005 #0006 if (deallocChunk_->blocksAvailable_ == numBlocks_) { #0007 // deallocChunk_ 目前完全 free, 該 release 它嗎? #0008 // 注意,以㆘㆔種情況都重新設定了allocChunk_ #0009 Chunk& lastChunk = chunks_.back(); //標示出最後㆒個 #0010 //如果 "最後㆒個" 就是 "當前回收者" #0011 if (&lastChunk == deallocChunk_) { #0012 // 檢查是否擁有兩個 last chunks empty #0013 // 這只需往 front 方向看前㆒個 chunk 便知. #0014 if (chunks_.size() > 1 && #0015 deallocChunk_[-1].blocksAvailable_ == numBlocks_) { #0016 // 是的,有 2 free chunks, 拋棄最後㆒個 #0017 lastChunk.Release(); #0018 chunks_.pop_back(); #0019 allocChunk_ = deallocChunk_ = &chunks_.front(); #0020 } #0021 return; #0022 } #0023 blocksAvailable_ firstAvailableBlock_ pData_ 1 m 2 待刪之 p 已確定 落在這個 chunk #0024 if (lastChunk.blocksAvailable_ == numBlocks_) { #0025 //2 free chunks, 拋棄最後㆒個 #0026 lastChunk.Release(); #0027 chunks_.pop_back(); #0028 allocChunk_ = deallocChunk_; #0029 } #0030 else { #0031 //將free chunk 與最後㆒個chunk 對調 #0032 std::swap(*deallocChunk_, lastChunk); #0033 allocChunk_ = &chunks_.back(); #0034 } #0035 } #0036 } #0024 if (lastChunk.blocksAvailable_ == numBlocks_) { #0025 //2 free chunks, 拋棄最後㆒個 #0026 lastChunk.Release(); #0027 chunks_.pop_back(); #0028 allocChunk_ = deallocChunk_; #0029 } #0030 else { #0031 //將free chunk 與最後㆒個chunk 對調 #0032 std::swap(*deallocChunk_, lastChunk); #0033 allocChunk_ = &chunks_.back(); #0034 } #0035 } #0036 } 這裡殺掉deallocChunk_ 似乎較好, 這就可以保持最後㆒個 chunk 為 free 以目前實作來看,似乎有可能某種情況 ㆘不歸還chunk 給系統。已證實。 deallocChunk_deallocChunk_ 1 2 3 改善之道:(1) 每次將 free chunk 置換到最後。(2) 每 再有 free chunk,檢查最後 chunk 是否為 free,若是, 殺掉當前 free chunk,即可。不必管什麼「最後連續 兩個 free chunks」這等瑣事。 ★ pop_back() 並不歸還記憶體 (vector 從不縮減空間) http://jjhou.csdn.net jjhou.928@gmail.com 85 Ref. MCD chap4, and Loki smallObj.cppLoki small object allocator, level 3 class SmallObjAllocator { private: typedef std::vector Pool; Pool pool_; FixedAllocator* pLastAlloc_; FixedAllocator* pLastDealloc_; std::size_t chunkSize_; std::size_t maxObjectSize_; public: void* Allocate(std::size_t nBytes); void Deallocate(void* p, std::size_t size); }; class SmallObjAllocator { private: typedef std::vector Pool; Pool pool_; FixedAllocator* pLastAlloc_; FixedAllocator* pLastDealloc_; std::size_t chunkSize_; std::size_t maxObjectSize_; public: void* Allocate(std::size_t nBytes); void Deallocate(void* p, std::size_t size); }; f48 f32 f30 f16 f12 pool_ 這有點像 SGI allocator 的 free_list[16] 但此處個數不限 16,區塊 大小亦不限為 8 倍數 ... 負責 12 bytes 區塊 的配 置和歸 還 負責 32 bytes 區塊 的配 置和歸 還 負責 48 bytes 區塊 的配 置和歸 還 SmallObjAllocator myAlloc(2048,256); // 第㆒參數表 ChunkSize // 第㆓參數表 maxObjSize // 這樣的彈性表示 // 它可產出「區塊大小低於 maxObjSize」 // 的任何 FixedAllocator 出來。 SmallObjAllocator myAlloc(2048,256); // 第㆒參數表 ChunkSize // 第㆓參數表 maxObjSize // 這樣的彈性表示 // 它可產出「區塊大小低於 maxObjSize」 // 的任何 FixedAllocator 出來。 以 cache 和 binary search 強化 pool_ 的 搜尋速 度 每個 元素都 是 FixedAllocator http://jjhou.csdn.net jjhou.928@gmail.com 86 Ref. MCD chap4, and Loki smallObj.cppLoki small object allocator, level 3, 實作實作實作實作 SmallObjAllocator::SmallObjAllocator( std::size_t chunkSize, std::size_t maxObjectSize) : pLastAlloc_(0), pLastDealloc_(0) , chunkSize_(chunkSize), maxObjectSize_(maxObjectSize) { } SmallObjAllocator::SmallObjAllocator( std::size_t chunkSize, std::size_t maxObjectSize) : pLastAlloc_(0), pLastDealloc_(0) , chunkSize_(chunkSize), maxObjectSize_(maxObjectSize) { } #01 void* SmallObjAllocator::Allocate(std::size_t numBytes) #02 { #03 if (numBytes > maxObjectSize_) //區塊 大小超 過服務 範圍 #04 return operator new(numBytes); //由 global operator new 服務 #05 #06 if (pLastAlloc_ && //㆖次 那 FixedAllocator 合適 否 #07 pLastAlloc_->BlockSize() == numBytes) { #08 return pLastAlloc_->Allocate(); //從 ㆗取區 塊 #09 } //至此 表示㆖ 次那個 FixedAllocator 不適 當 #10 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); //找出 適當位 置 #11 if (i == pool_.end() || i->BlockSize() != numBytes) { #12 i = pool_.insert(i, FixedAllocator(numBytes)); //建立 a new FixedAllocator(其內尚 無 chunk) 並插入vector #13 pLastDealloc_ = &*pool_.begin(); #14 } #15 pLastAlloc_ = &*i; //取正確 FixedAllocator 的位 址 #16 return pLastAlloc_->Allocate(); //從㆗取 區塊 #17 } #01 void* SmallObjAllocator::Allocate(std::size_t numBytes) #02 { #03 if (numBytes > maxObjectSize_) //區塊 大小超 過服務 範圍 #04 return operator new(numBytes); //由 global operator new 服務 #05 #06 if (pLastAlloc_ && //㆖次 那 FixedAllocator 合適 否 #07 pLastAlloc_->BlockSize() == numBytes) { #08 return pLastAlloc_->Allocate(); //從 ㆗取區 塊 #09 } //至此 表示㆖ 次那個 FixedAllocator 不適 當 #10 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end(), numBytes); //找出 適當位 置 #11 if (i == pool_.end() || i->BlockSize() != numBytes) { #12 i = pool_.insert(i, FixedAllocator(numBytes)); //建立 a new FixedAllocator(其內尚 無 chunk) 並插入vector #13 pLastDealloc_ = &*pool_.begin(); #14 } #15 pLastAlloc_ = &*i; //取正確 FixedAllocator 的位 址 #16 return pLastAlloc_->Allocate(); //從㆗取 區塊 #17 } #01 // Deallocates memory previously allocated with Allocate #02 // (undefined behavior if you pass any other pointer) #03 void SmallObjAllocator::Deallocate(void* p, std::size_t numByt #04 { //若超過 服務範 圍,轉 給 global operator delete() 去做 #05 if (numBytes > maxObjectSize_) return operator delete(p); #06 //標定者 為適當 之 FixedAllocator,令 它 Deallocate(p). #07 if (pLastDealloc_ && pLastDealloc_->BlockSize() == #08 numBytes) { #09 pLastDealloc_->Deallocate(p); #10 return; #11 } //至此 ,以㆘ 搜尋最 適當之 FixedAllocator( ㆒定有 ) #12 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end( #13 numBytes); #14 pLastDealloc_ = &*i; //取其位 址 #15 pLastDealloc_->Deallocate(p); //令它 Deallocate(p) #16 } #01 // Deallocates memory previously allocated with Allocate #02 // (undefined behavior if you pass any other pointer) #03 void SmallObjAllocator::Deallocate(void* p, std::size_t numByt #04 { //若超過 服務範 圍,轉 給 global operator delete() 去做 #05 if (numBytes > maxObjectSize_) return operator delete(p); #06 //標定者 為適當 之 FixedAllocator,令 它 Deallocate(p). #07 if (pLastDealloc_ && pLastDealloc_->BlockSize() == #08 numBytes) { #09 pLastDealloc_->Deallocate(p); #10 return; #11 } //至此 ,以㆘ 搜尋最 適當之 FixedAllocator( ㆒定有 ) #12 Pool::iterator i = std::lower_bound(pool_.begin(), pool_.end() #13 numBytes); #14 pLastDealloc_ = &*i; //取其位 址 #15 pLastDealloc_->Deallocate(p); //令它 Deallocate(p) #16 } Q: lower_bound() 操作 的對象 應該是sorted, 也就需 要 op<, 但 FixedAllocator 沒有 op< 呀! A: 有的 ,是個in-class inline: bool operator<(size_t rhs) const { return blockSize_ < rhs; } http://jjhou.csdn.net jjhou.928@gmail.com 89 先前提出的疑問,新版有修改。 1. 計算 chunk 內的 blocks 數量 FixedAllocator::FixedAllocator() : blockSize_( 0 ) , numBlocks_( 0 ) , chunks_( 0 ) , allocChunk_( NULL ) , deallocChunk_( NULL ) , emptyChunk_( NULL ) { } FixedAllocator::FixedAllocator() : blockSize_( 0 ) , numBlocks_( 0 ) , chunks_( 0 ) , allocChunk_( NULL ) , deallocChunk_( NULL ) , emptyChunk_( NULL ) { } void FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize ) { assert( blockSize > 0 ); assert( pageSize >= blockSize ); blockSize_ = blockSize; std::size_t numBlocks = pageSize / blockSize; if ( numBlocks > MaxObjectsPerChunk_ ) //J: >256 numBlocks = MaxObjectsPerChunk_; else if ( numBlocks < MinObjectsPerChunk_ ) //J: < 8 numBlocks = MinObjectsPerChunk_; numBlocks_ = static_cast(numBlocks); assert(numBlocks_ == numBlocks); } void FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize ) { assert( blockSize > 0 ); assert( pageSize >= blockSize ); blockSize_ = blockSize; std::size_t numBlocks = pageSize / blockSize; if ( numBlocks > MaxObjectsPerChunk_ ) //J: >256 numBlocks = MaxObjectsPerChunk_; else if ( numBlocks < MinObjectsPerChunk_ ) //J: < 8 numBlocks = MinObjectsPerChunk_; numBlocks_ = static_cast(numBlocks); assert(numBlocks_ == numBlocks); } SmallObjAllocator::SmallObjAllocator( std::size_t pageSize, std::size_t maxObjectSize, std::size_t objectAlignSize ) : pool_( NULL ), maxSmallObjectSize_( maxObjectSize ), objectAlignSize_( objectAlignSize ) { assert( 0 != objectAlignSize ); const std::size_t allocCount = GetOffset( maxObjectSize, objectAlignSize ); pool_ = new FixedAllocator[ allocCount ]; for ( std::size_t i = 0; i < allocCount; ++i ) pool_[ i ].Initialize( ( i+1 ) * objectAlignSize, pageSize ); } SmallObjAllocator::SmallObjAllocator( std::size_t pageSize, std::size_t maxObjectSize, std::size_t objectAlignSize ) : pool_( NULL ), maxSmallObjectSize_( maxObjectSize ), objectAlignSize_( objectAlignSize ) { assert( 0 != objectAlignSize ); const std::size_t allocCount = GetOffset( maxObjectSize, objectAlignSize ); pool_ = new FixedAllocator[ allocCount ]; for ( std::size_t i = 0; i < allocCount; ++i ) pool_[ i ].Initialize( ( i+1 ) * objectAlignSize, pageSize ); } 不同 於以往 ,現在 必須指 定 3 個參 數 /// Calculates index into array where /// a FixedAllocator of numBytes is located. inline std::size_t GetOffset( std::size_t numBytes, std::size_t alignment ) { const std::size_t alignExtra = alignment-1; return ( numBytes + alignExtra ) / alignment; } /// Calculates index into array where /// a FixedAllocator of numBytes is located. inline std::size_t GetOffset( std::size_t numBytes, std::size_t alignment ) { const std::size_t alignExtra = alignment-1; return ( numBytes + alignExtra ) / alignment; } 若傳入 (253, 4) 得 64 若傳入 (254, 4) 得 64 若傳入 (255, 4) 得 64 若傳入 (256, 4) 得 64 預先 要了㆒ 大塊 array, 為每 個元素 (FixedAllocator object) 計算 好基本 資料 SmallObjAllocator::~SmallObjAllocator( void ) { delete [ ] pool_; } SmallObjAllocator::~SmallObjAllocator( void ) { delete [ ] pool_; } Loki-0.1.6 http://jjhou.csdn.net jjhou.928@gmail.com 90 先前提出的疑問,新版有修改。 2. 搜尋 deallocated block 該落入哪個 chunk Chunk * FixedAllocator::VicinityFind( void * p ) const { if ( chunks_.empty() ) return NULL; assert(deallocChunk_); const std::size_t chunkLength = numBlocks_ * blockSize_; Chunk * lo = deallocChunk_; Chunk * hi = deallocChunk_ + 1; const Chunk * loBound = &chunks_.front(); const Chunk * hiBound = &chunks_.back() + 1; // Special case: deallocChunk_ is the last in the array if (hi == hiBound) hi = NULL; for (;;) { if (lo) { if ( lo->HasBlock( p, chunkLength ) ) return lo; if ( lo == loBound ) { lo = NULL; if ( NULL == hi ) break; } else --lo; } if (hi) { if ( hi->HasBlock( p, chunkLength ) ) return hi; if ( ++hi == hiBound ) { hi = NULL; if ( NULL == lo ) break; } } } return NULL; } Chunk * FixedAllocator::VicinityFind( void * p ) const { if ( chunks_.empty() ) return NULL; assert(deallocChunk_); const std::size_t chunkLength = numBlocks_ * blockSize_; Chunk * lo = deallocChunk_; Chunk * hi = deallocChunk_ + 1; const Chunk * loBound = &chunks_.front(); const Chunk * hiBound = &chunks_.back() + 1; // Special case: deallocChunk_ is the last in the array if (hi == hiBound) hi = NULL; for (;;) { if (lo) { if ( lo->HasBlock( p, chunkLength ) ) return lo; if ( lo == loBound ) { lo = NULL; if ( NULL == hi ) break; } else --lo; } if (hi) { if ( hi->HasBlock( p, chunkLength ) ) return hi; if ( ++hi == hiBound ) { hi = NULL; if ( NULL == lo ) break; } } } return NULL; } bool FixedAllocator::Deallocate( void * p, Chunk * hint ) { assert(!chunks_.empty()); assert(&chunks_.front() <= deallocChunk_); assert(&chunks_.back() >= deallocChunk_); assert( &chunks_.front() <= allocChunk_ ); assert( &chunks_.back() >= allocChunk_ ); assert( CountEmptyChunks() < 2 ); Chunk * foundChunk = ( NULL == hint ) ? VicinityFind( p ) : hint; if ( NULL == foundChunk ) return false; assert( foundChunk->HasBlock( p, numBlocks_ * blockSize_ ) ); #ifdef LOKI_CHECK_FOR_CORRUPTION if ( foundChunk->IsCorrupt( numBlocks_, blockSize_, true ) ) { assert( false ); return false; } if ( foundChunk->IsBlockAvailable( p, numBlocks_, blockSize_ ) ) { assert( false ); return false; } #endif deallocChunk_ = foundChunk; DoDeallocate(p); assert( CountEmptyChunks() < 2 ); return true; } bool FixedAllocator::Deallocate( void * p, Chunk * hint ) { assert(!chunks_.empty()); assert(&chunks_.front() <= deallocChunk_); assert(&chunks_.back() >= deallocChunk_); assert( &chunks_.front() <= allocChunk_ ); assert( &chunks_.back() >= allocChunk_ ); assert( CountEmptyChunks() < 2 ); Chunk * foundChunk = ( NULL == hint ) ? VicinityFind( p ) : hint; if ( NULL == foundChunk ) return false; assert( foundChunk->HasBlock( p, numBlocks_ * blockSize_ ) ); #ifdef LOKI_CHECK_FOR_CORRUPTION if ( foundChunk->IsCorrupt( numBlocks_, blockSize_, true ) ) { assert( false ); return false; } if ( foundChunk->IsBlockAvailable( p, numBlocks_, blockSize_ ) ) { assert( false ); return false; } #endif deallocChunk_ = foundChunk; DoDeallocate(p); assert( CountEmptyChunks() < 2 ); return true; } Loki-0.1.6 http://jjhou.csdn.net jjhou.928@gmail.com 95 STL, Loki, MFC, Boost 的安裝的安裝的安裝的安裝 •STL 附隨編譯器而來。client 只需含入適當的 headers 即可 使用 它。通常 client 使用 containers 而由後者使用 allocator。 client 絕少直 接 用 ㆖ allocator( 但不是 不可 以,例如為 Foo 設 計 operator new/delete 時 就可直接在其內使用 allocator) •Loki 無需安裝,client 只需含入適當的 headers 即可 使用 它。 •MFC 需要安裝,client 除了含入適當的 headers 外還 需安裝 好的 DLL 在 runtime 提供服務。 •Boost 需要安裝,以便產生若干 .dll 和 .lib。但若沒有安裝, 只要能含入適當的 headers 還 是 能正常使用 某些 libraries(包 括 Boost.Pool) http://jjhou.csdn.net jjhou.928@gmail.com 96 Pooled Allocation 與與與與 multi-threads •MFC 對 multi-threads 有支援 (CFixedAlloc ㆗使用 Critical Section) •Loki SmallObject (level4) 對 multi-threads 提供了介面, 但 Loki 本身只實現出 SingleThreaded 這㆒ 個 policy. •std::alloc 支援 POSIX threads 和 WIN32 threads ( 介面是 有支援啦,實際源碼也 有支援嗎?有!見㆘) •Boost 如何? http://jjhou.csdn.net jjhou.928@gmail.com 97 multi-threads in std::alloc (stl_alloc.h) #ifdef __STL_WIN32THREADS # include #endif #if !defined(__STL_PTHREADS) && !defined(_NOTHREADS) \ && !defined(__STL_SGI_THREADS) && !defined(__STL_WIN32THREADS) # define _NOTHREADS #endif # ifdef __STL_PTHREADS // POSIX Threads // This is dubious, since this is likely to be a high contention // lock. Performance may not be adequate. # include # define __NODE_ALLOCATOR_LOCK \ if (threads) pthread_mutex_lock(&__node_allocator_lock) # define __NODE_ALLOCATOR_UNLOCK \ if (threads) pthread_mutex_unlock(&__node_allocator_lock) # define __NODE_ALLOCATOR_THREADS true # define __VOLATILE volatile // Needed at -O3 on SGI # endif ... // 以㆘是第㆓級配置器。 // 注意,無「template型別參數」,且第㆓參數完全沒派㆖用場 template class __default_alloc_template { 1 http://jjhou.csdn.net jjhou.928@gmail.com 98 multi-threads in std::alloc (stl_alloc.h) # ifdef __STL_WIN32THREADS // The lock needs to be initialized by constructing an allocator // objects of the right type. We do that here explicitly for alloc. # define __NODE_ALLOCATOR_LOCK \ EnterCriticalSection(&__node_allocator_lock) # define __NODE_ALLOCATOR_UNLOCK \ LeaveCriticalSection(&__node_allocator_lock) # define __NODE_ALLOCATOR_THREADS true # define __VOLATILE volatile // may not be needed # endif /* WIN32THREADS */ # ifdef __STL_WIN32THREADS // The lock needs to be initialized by constructing an allocator // objects of the right type. We do that here explicitly for alloc. # define __NODE_ALLOCATOR_LOCK \ EnterCriticalSection(&__node_allocator_lock) # define __NODE_ALLOCATOR_UNLOCK \ LeaveCriticalSection(&__node_allocator_lock) # define __NODE_ALLOCATOR_THREADS true # define __VOLATILE volatile // may not be needed # endif /* WIN32THREADS */ # ifdef __STL_SGI_THREADS // This should work without threads, with sproc threads, or with // pthreads. It is suboptimal in all cases. // It is unlikely to even compile on nonSGI machines. extern "C" { extern int __us_rsthread_malloc; } // The above is copied from malloc.h. Including // would be cleaner but fails with certain levels of standard // conformance. # define __NODE_ALLOCATOR_LOCK if (threads && __us_rsthread_malloc) \ { __lock(&__node_allocator_lock); } # define __NODE_ALLOCATOR_UNLOCK if (threads && __us_rsthread_malloc) \ { __unlock(&__node_allocator_lock); } # define __NODE_ALLOCATOR_THREADS true # define __VOLATILE volatile // Needed at -O3 on SGI # endif # ifdef __STL_SGI_THREADS // This should work without threads, with sproc threads, or with // pthreads. It is suboptimal in all cases. // It is unlikely to even compile on nonSGI machines. extern "C" { extern int __us_rsthread_malloc; } // The above is copied from malloc.h. Including // would be cleaner but fails with certain levels of standard // conformance. # define __NODE_ALLOCATOR_LOCK if (threads && __us_rsthread_malloc) \ { __lock(&__node_allocator_lock); } # define __NODE_ALLOCATOR_UNLOCK if (threads && __us_rsthread_malloc) \ { __unlock(&__node_allocator_lock); } # define __NODE_ALLOCATOR_THREADS true # define __VOLATILE volatile // Needed at -O3 on SGI # endif 2 3 http://jjhou.csdn.net jjhou.928@gmail.com 99 multi-threads in std::alloc (stl_alloc.h) # ifdef _NOTHREADS // Thread-unsafe # define __NODE_ALLOCATOR_LOCK # define __NODE_ALLOCATOR_UNLOCK # define __NODE_ALLOCATOR_THREADS false # define __VOLATILE # endif # ifdef _NOTHREADS // Thread-unsafe # define __NODE_ALLOCATOR_LOCK # define __NODE_ALLOCATOR_UNLOCK # define __NODE_ALLOCATOR_THREADS false # define __VOLATILE # endif 4 http://jjhou.csdn.net jjhou.928@gmail.com 100 multi-threads in std::alloc (stl_alloc.h) template // 第㆓級配置器 class __default_alloc_template { ... # ifdef __STL_SGI_THREADS static volatile unsigned long __node_allocator_lock; static void __lock(volatile unsigned long *); static inline void __unlock(volatile unsigned long *); # endif # ifdef __STL_PTHREADS static pthread_mutex_t __node_allocator_lock; # endif # ifdef __STL_WIN32THREADS static CRITICAL_SECTION __node_allocator_lock; static bool __node_allocator_lock_initialized; public: __default_alloc_template() { // This assumes the first constructor is called before threads // are started. if (!__node_allocator_lock_initialized) { InitializeCriticalSection(&__node_allocator_lock); __node_allocator_lock_initialized = true; } } private: # endif class lock { public: lock() { __NODE_ALLOCATOR_LOCK; } ~lock() { __NODE_ALLOCATOR_UNLOCK; } }; friend lass lo k template // 第㆓級配置器 class __default_alloc_template { ... # ifdef __STL_SGI_THREADS static volatile unsigned long __node_allocator_lock; static void __lock(volatile unsigned long *); static inline void __unlock(volatile unsigned long *); # endif # ifdef __STL_PTHREADS static pthread_mutex_t __node_allocator_lock; # endif # ifdef __STL_WIN32THREADS static CRITICAL_SECTION __node_allocator_lock; static bool __node_allocator_lock_initialized; public: __default_alloc_template() { // This assumes the first constructor is called before threads // are started. if (!__node_allocator_lock_initialized) { InitializeCriticalSection(&__node_allocator_lock); __node_allocator_lock_initialized = true; } } private: # endif class lock { public: lock() { __NODE_ALLOCATOR_LOCK; } ~lock() { __NODE_ALLOCATOR_UNLOCK; } }; friend lass lo k; 前面已顯示,針對 PTHREADS 或 WIN32THREADS 或 SGI_THREADS 各有各自的定義 5 8 6 7 http://jjhou.csdn.net jjhou.928@gmail.com 101 multi-threads in std::alloc (stl_alloc.h) template // 第㆓級配置器 class __default_alloc_template { ... public: /* n must be > 0 */ static void * allocate(size_t n) { obj * __VOLATILE * my_free_list; obj * __RESTRICT result; if (n > (size_t) __MAX_BYTES) { return(malloc_alloc::allocate(n)); } my_free_list = free_list + FREELIST_INDEX(n); // Acquire the lock here with a constructor call. // This ensures that it is released in exit or during stack unwinding. # ifndef _NOTHREADS /*REFERENCED*/ lock lock_instance; # endif result = *my_free_list; if (result == 0) { void *r = refill(ROUND_UP(n)); return r; } *my_free_list = result -> free_list_link; return (result); }; template // 第㆓級配置器 class __default_alloc_template { ... public: /* n must be > 0 */ static void * allocate(size_t n) { obj * __VOLATILE * my_free_list; obj * __RESTRICT result; if (n > (size_t) __MAX_BYTES) { return(malloc_alloc::allocate(n)); } my_free_list = free_list + FREELIST_INDEX(n); // Acquire the lock here with a constructor call. // This ensures that it is released in exit or during stack unwinding. # ifndef _NOTHREADS /*REFERENCED*/ lock lock_instance; # endif result = *my_free_list; if (result == 0) { void *r = refill(ROUND_UP(n)); return r; } *my_free_list = result -> free_list_link; return (result); }; 喚起 ctor lock::lock(). 如果在 WIN32THREADS 情況㆘( ) ,會喚起 EnterCriticalSection(&__node_allocator_lock) 如果在 PTHREADS 情況㆘( ) ,會喚起 pthread_mutex_lock(&__node_allocator_lock) 2 7 1 6 喚起 dtor lock::~lock(). 如果在 WIN32THREADS 情況㆘( ) ,會喚起 LeaveCriticalSection(&__node_allocator_lock) 如果在 PTHREADS 情況㆘( ) ,會喚起 pthread_mutex_unlock(&__node_allocator_lock) 2 7 1 6 http://jjhou.csdn.net jjhou.928@gmail.com 102 multi-threads in std::alloc (stl_alloc.h) template // 第㆓級配置器 class __default_alloc_template { ... public: /* p may not be 0 */ static void deallocate(void *p, size_t n) { obj *q = (obj *)p; obj * __VOLATILE * my_free_list; if (n > (size_t) __MAX_BYTES) { malloc_alloc::deallocate(p, n); return; } my_free_list = free_list + FREELIST_INDEX(n); // acquire lock # ifndef _NOTHREADS /*REFERENCED*/ lock lock_instance; # endif /* _NOTHREADS */ q -> free_list_link = *my_free_list; *my_free_list = q; // lock is released here } template // 第㆓級配置器 class __default_alloc_template { ... public: /* p may not be 0 */ static void deallocate(void *p, size_t n) { obj *q = (obj *)p; obj * __VOLATILE * my_free_list; if (n > (size_t) __MAX_BYTES) { malloc_alloc::deallocate(p, n); return; } my_free_list = free_list + FREELIST_INDEX(n); // acquire lock # ifndef _NOTHREADS /*REFERENCED*/ lock lock_instance; # endif /* _NOTHREADS */ q -> free_list_link = *my_free_list; *my_free_list = q; // lock is released here } 分析 同前頁 http://jjhou.csdn.net jjhou.928@gmail.com 103 MFC's CPlex Ref. MFC's afxplex_.h & plex.cpp CPlex object this nMax * cbElement void* CPlex::data() { return this+1; } ... CPlex 扮演 memory pool 角色 pNext pNext void CPlex::FreeDataChain() { CPlex* p = this; while (p != NULL) { BYTE* bytes = (BYTE*) p; CPlex* pNext = p->pNext; delete[] bytes; p = pNext; } //歸還 每㆒長 條 } void CPlex::FreeDataChain() { CPlex* p = this; while (p != NULL) { BYTE* bytes = (BYTE*) p; CPlex* pNext = p->pNext; delete[] bytes; p = pNext; } //歸還 每㆒長 條 } 01 struct CPlex 02 { 03 CPlex* pNext; 04 #if (_AFX_PACKING >= 8) 05 DWORD dwReserved[1]; // align on 8 byte boundary 06 #endif 07 void* data() { return this+1; } 08 static CPlex* PASCAL Create(CPlex*& head, 09 UINT nMax, UINT cbElement); 10 void FreeDataChain(); // free this one and links 11 }; 01 struct CPlex 02 { 03 CPlex* pNext; 04 #if (_AFX_PACKING >= 8) 05 DWORD dwReserved[1]; // align on 8 byte boundary 06 #endif 07 void* data() { return this+1; } 08 static CPlex* PASCAL Create(CPlex*& head, 09 UINT nMax, UINT cbElement); 10 void FreeDataChain(); // free this one and links 11 }; 全 部 清 除 每次 配置㆒ 長條 CPlex 只 負責配 置㆒大 塊 memory 並串接 ,不負 責分割 。 細切 分割之 事由使 用者( 如 Collection classes, CFixedAlloc)負 責。 注意 , 有new也有delete pass by reference to pointer to CPlex. 其作用和 CPlex** 相同, 形式不同而已 此函式被㆖層呼叫;見 CPtrList::RemoveAll() 和 CFixedAlloc::FreeAll(). 前者是當 list 的節點全部收回後被呼叫, 後者是被 dtor 呼叫. FreeDataChain() 在呼 叫 delete[] 前先 將 pointer 轉 型使為 BYTE* 不這 麼做難 道會有 問題? 見㆘頁 。 01 CPlex* PASCAL CPlex::Create(CPlex*& pHead, 02 UINT nMax, UINT cbElement 03 { //配置 記憶體 後順便 鏈接起 來. 04 CPlex* p = (CPlex*) new 05 BYTE[sizeof(CPlex) + nMax * cbElement]; 06 p->pNext = pHead; 07 pHead = p; // change head (adds in reverse order) 08 return p; 09 } 01 CPlex* PASCAL CPlex::Create(CPlex*& pHead, 02 UINT nMax, UINT cbElement 03 { //配置 記憶體 後順便 鏈接起 來. 04 CPlex* p = (CPlex*) new 05 BYTE[sizeof(CPlex) + nMax * cbElement]; 06 p->pNext = pHead; 07 pHead = p; // change head (adds in reverse order) 08 return p; 09 } Create() 會建立起㆒條 free list 為 某特定大小服務 http://jjhou.csdn.net jjhou.928@gmail.com 104 MFC's CPlex this nMax * cbElement void* CPlex::data() { return this+1; } ... CPlex 扮演 memory pool 角色 pNext pNext void CPlex::FreeDataChain() { CPlex* p = this; while (p != NULL) { BYTE* bytes = (BYTE*) p; CPlex* pNext = p->pNext; delete[] bytes; p = pNext; } //歸還 每㆒長 條 } void CPlex::FreeDataChain() { CPlex* p = this; while (p != NULL) { BYTE* bytes = (BYTE*) p; CPlex* pNext = p->pNext; delete[] bytes; p = pNext; } //歸還 每㆒長 條 } 全 部 清 除 FreeDataChain() 在呼 叫 delete[] 前先 將 pointer 轉 型使為 BYTE* 不這 麼做難 道會有 問題? 測試 :試著 寫出 Foo 和 Goo 的 operator delete[],然 後 new Foo[10]; 所 得轉型 為 *Goo, 看看傳 入的大 小是多 少。 class Foo { public: Foo() { } void* operator new[](size_t n) { cout << "operator new[] " << n << endl; return ::operator new[](n); //VC error } void operator delete[](void* p, size_t n) { cout << "operator delete[] " << n << endl; ::operator delete[](p); //VC error } private: int x1,x2,x3; }; class Foo { public: Foo() { } void* operator new[](size_t n) { cout << "operator new[] " << n << endl; return ::operator new[](n); //VC error } void operator delete[](void* p, size_t n) { cout << "operator delete[] " << n << endl; ::operator delete[](p); //VC error } private: int x1,x2,x3; }; class Goo { public: Goo() { } void* operator new[](size_t n) { cout << "Goo operator new[] " << n << endl; return ::operator new[](n); //VC error } void operator delete[](void* p, size_t n) { cout << "Goo operator delete[] " << n << endl; ::operator delete[](p); //VC error } private: int x1; }; class Goo { public: Goo() { } void* operator new[](size_t n) { cout << "Goo operator new[] " << n << endl; return ::operator new[](n); //VC error } void operator delete[](void* p, size_t n) { cout << "Goo operator delete[] " << n << endl; ::operator delete[](p); //VC error } private: int x1; }; cout << "sizeof Foo = " << sizeof(Foo); cout << "sizeof Goo = " << sizeof(Goo); Foo* pf = new Foo[3]; Goo* pg = (Goo*)pf; delete[] pg; cout << "sizeof Foo = " << sizeof(Foo); cout << "sizeof Goo = " << sizeof(Goo); Foo* pf = new Foo[3]; Goo* pg = (Goo*)pf; delete[] pg; 此例 驗證, 當我們 以 X type 'new[]'㆒個 array, delete[] 時給予 的 pointer 也 ㆒定得 是 X type, 否則 編譯器 對大小 的計算 將會出 錯 sizeof Foo = 12 sizeof Goo = 4 operator new[] 44 Goo operator delete[] 20 sizeof Foo = 12 sizeof Goo = 4 operator new[] 44 Goo operator delete[] 20 G291 44=12*3+8 20=4*3+8 http://jjhou.csdn.net jjhou.928@gmail.com 105 MFC's CPlex CPlex 被用於 兩種場 合。 ■ ㆒是 被各種 容器直 接使用 (例如 CPtrList::m_pBlocks)。 每個容 器因此 形成自 己的㆒ 個特 定尺 寸(例 如 sizeof(CPtrList::CNode))的 free list. 這些 容器有 自己的 counter(例 如 CPtrList::m_nCount),隨 時可知 元素個 數。因 此當容 器釋放 元素( 例如 CPtrList::FreeNode()) 後檢 查 counter 如 果其值 為 0 就 把所有 空間還 給 OS(呼 叫 CPlex::FreeDataChain())。 ■ 另㆒ 是被 CFixedAlloc 使用 ,用來 提供給 ㆔川㆕ 海五湖 的各種 需求。 各種需 求尺寸 會在 CFixedAlloc ctor 傳入 並記錄 。每個 客戶使 用 CFixedAlloc( 也就是 建立起 ㆒個 CFixedAlloc object)其 實就是 使用其 內的㆒ 條特定 區塊尺 寸的 free list。各 個 CFixedAlloc objects 不 相混淆 , 就算 兩個 clients 用的區 塊尺寸 相同, 也不混 淆,因 為形成 的是兩 個區塊 尺寸相 同的 free lists。 CFixedAlloc 只在 dtor ㆗才 釋放所 有空間 給 OS(呼 叫 CPlex::FreeDataChain())。 •CPtrList 的 NewNode(), FreeNode, RemoveAll() 相當 於 CFixedAlloc 的 Alloc(), Free(), FreeAll(). •容器 其實完 全可以 採用 CFixedAlloc 而 不要直 接使用 底層的 CPlex. •不論 是 CPtrList 或 CFixedAlloc 對 CPlex 的使用 (用法 相同) ,都沒 有能夠分段檢 視並歸 還 給 OS,所以 也屬霸 道,情 況和 std::alloc 差不多 。 http://jjhou.csdn.net jjhou.928@gmail.com 106 MFC's CPtrList CPlex object pos pospos CPtrList object : GetHeadPosition() GetNext() m_pNodeHead m_pNodeTail m_pNodeFree m_pBlocks m_nBlockSize (default 10) CNode object in free list void* CPlex::data() { return this+1; } used CNode object (in "Ptr-List") interface implementation + m_nBlockSize - 1 ... ... CPlex扮演 memory pool 角色 個數 :m_nCount Ref. MFC's afxcoll.h & list_p.cpp CPtrList 的每 個節點 都來自 pooled allocator CPlex,因此 都不 帶 cookie。 http://jjhou.csdn.net jjhou.928@gmail.com 107 MFC's CPtrList 01 class CPtrList : public CObject 02 { 03 DECLARE_DYNAMIC(CPtrList) 04 protected: 05 struct CNode { 06 CNode* pNext; 07 CNode* pPrev; 08 void* data; 09 }; 10 public: 11 CPtrList(int nBlockSize = 10); //預 設每個CPlex object 內有10個 CNodes. 12 ~CPtrList(); // 注意 ,MFC collection classes 的 dtor 都是 non-virtual 13 void RemoveAll(); 14 POSITION AddTail(void* newElement); 15 protected: 16 CNode* m_pNodeFree; // garbage list (free list) 17 CNode* m_pNodeHead; 18 CNode* m_pNodeTail; 19 int m_nCount; //元素 個數 20 struct CPlex* m_pBlocks; 21 int m_nBlockSize; //每個 CPlex 內將 有多少 個 CNodes 22 23 CNode* NewNode(CNode*, CNode*); 24 void FreeNode(CNode*); 25 }; 01 class CPtrList : public CObject 02 { 03 DECLARE_DYNAMIC(CPtrList) 04 protected: 05 struct CNode { 06 CNode* pNext; 07 CNode* pPrev; 08 void* data; 09 }; 10 public: 11 CPtrList(int nBlockSize = 10); //預 設每個CPlex object 內有10個 CNodes. 12 ~CPtrList(); // 注意 ,MFC collection classes 的 dtor 都是 non-virtual 13 void RemoveAll(); 14 POSITION AddTail(void* newElement); 15 protected: 16 CNode* m_pNodeFree; // garbage list (free list) 17 CNode* m_pNodeHead; 18 CNode* m_pNodeTail; 19 int m_nCount; //元素 個數 20 struct CPlex* m_pBlocks; 21 int m_nBlockSize; //每個 CPlex 內將 有多少 個 CNodes 22 23 CNode* NewNode(CNode*, CNode*); 24 void FreeNode(CNode*); 25 }; pNextpPrev data 01 CPtrList::CPtrList(int nBlockSize /* =10 */ ) 02 { 03 m_nCount = 0; 04 m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; 05 m_pBlocks = NULL; 06 m_nBlockSize = nBlockSize; 07 } 08 CPtrList::~CPtrList() 09 { 10 RemoveAll(); 11 ASSERT(m_nCount == 0); 12 } 01 CPtrList::CPtrList(int nBlockSize /* =10 */ ) 02 { 03 m_nCount = 0; 04 m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; 05 m_pBlocks = NULL; 06 m_nBlockSize = nBlockSize; 07 } 08 CPtrList::~CPtrList() 09 { 10 RemoveAll(); 11 ASSERT(m_nCount == 0); 12 } 01 POSITION CPtrList::AddTail(void* newElement) 02 { 03 CNode* pNewNode = NewNode(m_pNodeTail, NULL); 04 pNewNode->data = newElement; 05 if (m_pNodeTail != NULL) 06 m_pNodeTail->pNext = pNewNode; 07 else 08 m_pNodeHead = pNewNode; 09 m_pNodeTail = pNewNode; 10 return (POSITION) pNewNode; 11 } 01 POSITION CPtrList::AddTail(void* newElement) 02 { 03 CNode* pNewNode = NewNode(m_pNodeTail, NULL); 04 pNewNode->data = newElement; 05 if (m_pNodeTail != NULL) 06 m_pNodeTail->pNext = pNewNode; 07 else 08 m_pNodeHead = pNewNode; 09 m_pNodeTail = pNewNode; 10 return (POSITION) pNewNode; 11 } 預設 情況㆘ 這個 CPtrList 每次 對 CPlex 要求 10 個 blocks,然 後㆒ ㆒從這10個取 block 來用 (當 做㆒個 CNode object)。 ㆘頁 CPtrList, CObList, CStringList, CMapWordToPtr, CMapPtrToWord, CMapPtrToPtr, CMapWordToOb, CMapStringToPtr, CMapStringToOb, CMapStringToString 其內 都有這 樣㆒個 struct CPlex* m_pBlocks 這些 MFC collection classes 並不使 用 CFixedAlloc,而是 直接使 用 CPlex。只 有 CString 使用 CFixedAlloc。 http://jjhou.csdn.net jjhou.928@gmail.com 108 MFC's CPtrList CPtrList::CNode* CPtrList::NewNode(CPtrList::CNode* pPrev, CPtrList::CNode* pNext) { if (m_pNodeFree == NULL) { // add another block CPlex* pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CNode)); // chain them into free list CNode* pNode = (CNode*) pNewBlock->data(); // free in reverse order to make it easier to debug pNode += m_nBlockSize - 1; for (int i = m_nBlockSize-1; i >= 0; i--, pNode--) { pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; } } CPtrList::CNode* pNode = m_pNodeFree; m_pNodeFree = m_pNodeFree->pNext; pNode->pPrev = pPrev; pNode->pNext = pNext; m_nCount++; //累計 CPtrList 的 元素總 量 pNode->data = 0; // start with zero return pNode; } CPtrList::CNode* CPtrList::NewNode(CPtrList::CNode* pPrev, CPtrList::CNode* pNext) { if (m_pNodeFree == NULL) { // add another block CPlex* pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize, sizeof(CNode)); // chain them into free list CNode* pNode = (CNode*) pNewBlock->data(); // free in reverse order to make it easier to debug pNode += m_nBlockSize - 1; for (int i = m_nBlockSize-1; i >= 0; i--, pNode--) { pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; } } CPtrList::CNode* pNode = m_pNodeFree; m_pNodeFree = m_pNodeFree->pNext; pNode->pPrev = pPrev; pNode->pNext = pNext; m_nCount++; //累計 CPtrList 的 元素總 量 pNode->data = 0; // start with zero return pNode; } 這個 函式類 似 CFixedAlloc::Alloc() #01 #02 #03 #04 #05 #06 #07 #08 #09 #10 #11 #12 #13 #14 #15 #16 #17 #18 #19 #20 #21 #22 #23 #24 #25 #26 #27 #28 m_pBlocks void* CPlex::data() { return this+1; } 個數 :m_nBlockSize sizeof(CNode) m_nBlockSize * sizeof(CNode)...pNext http://jjhou.csdn.net jjhou.928@gmail.com 109 MFC's CPtrList void CPtrList::FreeNode(CPtrList::CNode* pNode) { // 垃圾 回收, 把被釋 放的元 素串起 來。直 到最後 才會㆒ 次總清 。 pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; m_nCount--; //累計 CPtrList 的元素 總量 // 如果 這個被 釋元素 是 linked list (CPtrList) 的最 後㆒個 元素, 就將 pool 完全清 除 if (m_nCount == 0) RemoveAll(); // 總清 理 } void CPtrList::FreeNode(CPtrList::CNode* pNode) { // 垃圾 回收, 把被釋 放的元 素串起 來。直 到最後 才會㆒ 次總清 。 pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; m_nCount--; //累計 CPtrList 的元素 總量 // 如果 這個被 釋元素 是 linked list (CPtrList) 的最 後㆒個 元素, 就將 pool 完全清 除 if (m_nCount == 0) RemoveAll(); // 總清 理 } void CPtrList::RemoveAll() { // CPtrList 是以 垃圾收 集方式 來收集 垃圾, 最後才㆒次性 總清。 m_nCount = 0; m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; m_pBlocks->FreeDataChain(); //這 裡便是 總清的 ㆞點。 m_pBlocks = NULL; // jjhou: 這裡應 該㆒㆒ 釋放每 個元素 (指標 )所指 向的記 憶體嗎 ? // 似乎 不必, 因為使 用者每 次呼叫 RemoveAt() 後, 如有必 要, // 自己 得做 delete 動作。 如無必 要,當 然就不 做了。 } void CPtrList::RemoveAll() { // CPtrList 是以 垃圾收 集方式 來收集 垃圾, 最後才㆒次性 總清。 m_nCount = 0; m_pNodeHead = m_pNodeTail = m_pNodeFree = NULL; m_pBlocks->FreeDataChain(); //這 裡便是 總清的 ㆞點。 m_pBlocks = NULL; // jjhou: 這裡應 該㆒㆒ 釋放每 個元素 (指標 )所指 向的記 憶體嗎 ? // 似乎 不必, 因為使 用者每 次呼叫 RemoveAt() 後, 如有必 要, // 自己 得做 delete 動作。 如無必 要,當 然就不 做了。 } 這個 函式類 似 CFixedAlloc::Free() and/or CFixedAlloc::~CFixedAlloc() 這個 函式類 似 CFixedAlloc::FreeAll() #01 #02 #03 #04 #05 #06 #07 #08 #09 #10 #11 #01 #02 #03 #04 #05 #06 #07 #08 #09 #10 #11 #12 #13 http://jjhou.csdn.net jjhou.928@gmail.com 110 MFC pooled allocation 兩個兩個兩個兩個 主要主要主要主要 classes 的的的的關 係關係關係關係 CFixedAlloc m_pBlocks : CPlex* m_nAllocSize : UINT m_nBlockSize : UINT m_pNodeFree : CNode* Alloc() Free(...) FreeAll() CPlex pNext : CPlex* Create(...) FreeDataChain() 1..n http://jjhou.csdn.net jjhou.928@gmail.com 111 // fixalloc.h - declarations for fixed block allocator #include "afxplex_.h" class CFixedAlloc { public: CFixedAlloc(UINT nAllocSize, UINT nBlockSize = 64); UINT GetAllocSize() { return m_nAllocSize; } public: void* Alloc(); // return a chunk of memory of nAllocSize void Free(void* p); // free chunk of memory returned from Alloc void FreeAll(); // free everything allocated from this allocator public: ~CFixedAlloc(); protected: struct CNode { CNode* pNext; // only valid when in free list }; UINT m_nAllocSize; // size of each block from Alloc UINT m_nBlockSize; // number of blocks to get at a time CPlex* m_pBlocks; // linked list of blocks (is nBlocks*nAllocSize) CNode* m_pNodeFree; // first free node (NULL if no free nodes) CRITICAL_SECTION m_protect; }; // fixalloc.h - declarations for fixed block allocator #include "afxplex_.h" class CFixedAlloc { public: CFixedAlloc(UINT nAllocSize, UINT nBlockSize = 64); UINT GetAllocSize() { return m_nAllocSize; } public: void* Alloc(); // return a chunk of memory of nAllocSize void Free(void* p); // free chunk of memory returned from Alloc void FreeAll(); // free everything allocated from this allocator public: ~CFixedAlloc(); protected: struct CNode { CNode* pNext; // only valid when in free list }; UINT m_nAllocSize; // size of each block from Alloc UINT m_nBlockSize; // number of blocks to get at a time CPlex* m_pBlocks; // linked list of blocks (is nBlocks*nAllocSize) CNode* m_pNodeFree; // first free node (NULL if no free nodes) CRITICAL_SECTION m_protect; }; Ref. MFC's fixalloc.h & fixalloc.cpp CFixedAlloc::~CFixedAlloc() { FreeAll(); DeleteCriticalSection(&m_protect); } void CFixedAlloc::FreeAll() { EnterCriticalSection(&m_protect); m_pBlocks->FreeDataChain(); m_pBlocks = NULL; m_pNodeFree = NULL; LeaveCriticalSection(&m_protect); } CFixedAlloc::~CFixedAlloc() { FreeAll(); DeleteCriticalSection(&m_protect); } void CFixedAlloc::FreeAll() { EnterCriticalSection(&m_protect); m_pBlocks->FreeDataChain(); m_pBlocks = NULL; m_pNodeFree = NULL; LeaveCriticalSection(&m_protect); } 此前 ㆘頁 注意 :MFC collection classes 並不 使用 CFixedAlloc, 而是直 接使用 CPlex。 只有CString 使用 CFixedAlloc。 如果程式發展至「擁有多個 CPlex」, 那麼當 client 呼叫 CFixedAlloc::Free(p), 程式怎知 p 落在哪㆒個 CPlex 內呢? 它不知!它是跨 CPlexs 亂串。它在 FreeAll 前也沒有(能力)判斷是否都回收齊了。 MFC's CFixedAlloc // fixalloc.cpp - implementation of fixed block allocator #include "stdafx.h" #include "fixalloc.h" CFixedAlloc::CFixedAlloc(UINT nAllocSize, UINT nBlockSize) { m_nAllocSize = nAllocSize; //區塊 大小 m_nBlockSize = nBlockSize; //每次區 塊數 m_pNodeFree = NULL; m_pBlocks = NULL; InitializeCriticalSection(&m_protect); } // fixalloc.cpp - implementation of fixed block allocator #include "stdafx.h" #include "fixalloc.h" CFixedAlloc::CFixedAlloc(UINT nAllocSize, UINT nBlockSize) { m_nAllocSize = nAllocSize; //區塊 大小 m_nBlockSize = nBlockSize; //每次區 塊數 m_pNodeFree = NULL; m_pBlocks = NULL; InitializeCriticalSection(&m_protect); } void CFixedAlloc::Free(void* p) { if (p != NULL) { EnterCriticalSection(&m_protect); // simply return the node to the free list CNode* pNode = (CNode*)p; pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; LeaveCriticalSection(&m_protect); } } void CFixedAlloc::Free(void* p) { if (p != NULL) { EnterCriticalSection(&m_protect); // simply return the node to the free list CNode* pNode = (CNode*)p; pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; LeaveCriticalSection(&m_protect); } } #01 #02 #03 #04 #05 #06 #07 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 http://jjhou.csdn.net jjhou.928@gmail.com 112 MFC's CFixedAlloc void* CFixedAlloc::Alloc() // MFC allocator 的 對外窗 口 { EnterCriticalSection(&m_protect); if (m_pNodeFree == NULL) { //如 果沒有 區塊可 提供, 就配置 ㆒個 CPlex CPlex* pNewBlock = NULL; pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize, m_nAllocSize); // chain them into free list CNode* pNode = (CNode*)pNewBlock->data(); // free in reverse order to make it easier to debug (BYTE*&)pNode += (m_nAllocSize * m_nBlockSize) - m_nAllocSize; for (int i = m_nBlockSize-1; i >= 0; i--, (BYTE*&)pNode -= m_nAllocSize) { pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; } //切為 小塊( 鏈接起 來) } // remove the first available node from the free list void* pNode = m_pNodeFree; m_pNodeFree = m_pNodeFree->pNext; LeaveCriticalSection(&m_protect); return pNode; } void* CFixedAlloc::Alloc() // MFC allocator 的 對外窗 口 { EnterCriticalSection(&m_protect); if (m_pNodeFree == NULL) { //如 果沒有 區塊可 提供, 就配置 ㆒個 CPlex CPlex* pNewBlock = NULL; pNewBlock = CPlex::Create(m_pBlocks, m_nBlockSize, m_nAllocSize); // chain them into free list CNode* pNode = (CNode*)pNewBlock->data(); // free in reverse order to make it easier to debug (BYTE*&)pNode += (m_nAllocSize * m_nBlockSize) - m_nAllocSize; for (int i = m_nBlockSize-1; i >= 0; i--, (BYTE*&)pNode -= m_nAllocSize) { pNode->pNext = m_pNodeFree; m_pNodeFree = pNode; } //切為 小塊( 鏈接起 來) } // remove the first available node from the free list void* pNode = m_pNodeFree; m_pNodeFree = m_pNodeFree->pNext; LeaveCriticalSection(&m_protect); return pNode; } m_nBlockSize * m_nAllocSize...pNext void* CPlex::data() { return this+1; } 個數 :m_nBlockSize m_nAllocSize #01 #02 #03 #04 #05 #06 #07 #08 #09 #10 #11 #12 #13 #14 #15 #16 #17 #18 #19 #20 #21 #22 #23 #24 #25 #26 #27 http://jjhou.csdn.net jjhou.928@gmail.com 113 MFC's CFixedAlloc 7e07a4 7e07a87e07a0 7e0d34 7e0d387e0d30 0 7e07c8 0 m_pNodeFree CFixedAlloc allocator(sizeof(Foo),10); void* p[20]; for (int i=0; i<13; ++i) { p[i] = allocator.Alloc(); cout << p[i] << endl; } p[12] p[10] p[4] p[7] http://jjhou.csdn.net jjhou.928@gmail.com 114 MFC's CFixedAlloc 7e07a4 7e07a87e07a0 7e0d34 7e0d387e0d30 0 7e07c8 0 m_pNodeFree CFixedAlloc allocator(4,10); void* p[20]; for (int i=0; i<13; ++i) { p[i] = allocator.Alloc(); cout << p[i] << endl; } allocator.Free(p[4]); p[4] 歸還的那個就當做「㆘次將被配置」的那個 通常 ,歸還 的那個 就被當 做「㆘ 次將被 配置」 的那個 。這在 STL, Loki, MFC 都如此 。 但 Boost 不 如此。Boost 有㆒個 ordered_free()... 如果程式發展至「擁有多個 CPlex」,那麼當 client 呼叫 CFixedAlloc::Free(p), 程式怎知 p 落在哪㆒個 CPlex 內呢?它不知!它是跨 CPlexs 亂串。它在 FreeAll 前也沒有(能力)判斷是否都回收齊了。 http://jjhou.csdn.net jjhou.928@gmail.com 115 MFC's CFixedAlloc 7e07a4 7e07a87e07a0 7e0d34 7e0d387e0d30 0 7e07c8 0 m_pNodeFree CFixedAlloc allocator(4,10); void* p[20]; for (int i=0; i<13; ++i) { p[i] = allocator.Alloc(); cout << p[i] << endl; } allocator.Free(p[4]); allocator.Free(p[10]); p[10] 歸還的那個就當做「㆘次將被配置」的那個 http://jjhou.csdn.net jjhou.928@gmail.com 116 MFC's CFixedAlloc 7e07a4 7e07a87e07a0 7e0d34 7e0d387e0d30 0 7e07c8 0 CFixedAlloc allocator(4,10); void* p[20]; for (int i=0; i<13; ++i) { p[i] = allocator.Alloc(); cout << p[i] << endl; } allocator.Free(p[4]); allocator.Free(p[10]); allocator.Free(p[7]); p[7] m_pNodeFree 歸還的那個就當做「㆘次將被配置」的那個 http://jjhou.csdn.net jjhou.928@gmail.com 117 MFC's FIXED_ALLOC // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) \ public: \ void* operator new(size_t size) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ void* operator new(size_t, void* p) \ { return p; } \ void operator delete(void* p) { s_alloc.Free(p); } \ void* operator new(size_t size, LPCSTR, int) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ protected: \ static CFixedAlloc s_alloc \ // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) \ CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) \ // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) \ public: \ void* operator new(size_t size) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ void* operator new(size_t, void* p) \ { return p; } \ void operator delete(void* p) { s_alloc.Free(p); } \ void* operator new(size_t size, LPCSTR, int) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ protected: \ static CFixedAlloc s_alloc \ // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) \ CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) \ 每次 配置的 區塊個 數 沒用 到 #01 #02 #03 #04 #05 #06 #07 #08 #09 #10 #11 #12 #13 #14 #15 #16 #17 #18 #19 #20 #21 #22 #23 #24 #25 MFC's fixalloc.h #ifndef _DEBUG ... #else //!_DEBUG #define DECLARE_FIXED_ALLOC(class_name) // nothing in debug #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) // nothing in debug #endif //!_DEBUG 注意 :只有 non debug mode 才定 義 左邊 這兩個 macros。但為什 麼呢? http://jjhou.csdn.net jjhou.928@gmail.com 118 MFC's FIXED_ALLOC 在 CTempWnd, CTempImageList, CTempDC, CTempGdiObject, CTempMenu ㆗用 到。 // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) \ public: \ void* operator new(size_t size) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ void* operator new(size_t, void* p) \ { return p; } \ void operator delete(void* p) { s_alloc.Free(p); } \ void* operator new(size_t size, LPCSTR, int) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ protected: \ static CFixedAlloc s_alloc \ // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) \ CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) \ // DECLARE_FIXED_ALLOC -- used in class definition #define DECLARE_FIXED_ALLOC(class_name) \ public: \ void* operator new(size_t size) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ void* operator new(size_t, void* p) \ { return p; } \ void operator delete(void* p) { s_alloc.Free(p); } \ void* operator new(size_t size, LPCSTR, int) \ { \ ASSERT(size == s_alloc.GetAllocSize()); \ UNUSED(size); \ return s_alloc.Alloc(); \ } \ protected: \ static CFixedAlloc s_alloc \ // IMPLEMENT_FIXED_ALLOC -- used in class implementation file #define IMPLEMENT_FIXED_ALLOC(class_name, block_size) \ CFixedAlloc class_name::s_alloc(sizeof(class_name), block_size) \ 每次 配置的 區塊個 數 Ref. MFC's fixalloc.h & fixalloc.cpp class Foo { DECLARE_FIXED_ALLOC(Foo); private: long m_l; }; IMPLEMENT_FIXED_ALLOC(Foo, 10); class Foo { DECLARE_FIXED_ALLOC(Foo); private: long m_l; }; IMPLEMENT_FIXED_ALLOC(Foo, 10); 於是 ,Foo 擁有㆒ 個自己 的 (static)pooled allocator,每次 new Foo; 會喚 起 Foo::operator new() 向該 pooled allocator 要 求 10 個 區塊, 每個區 塊大小 是 sizeof(Foo)。傳 回1個餘 9 個 備㆘ 次使用 。 任何 class 只 要用了 這套 macros,便是 擁有㆒ 個 static pool allocator, 又重載 operator new 和 operator delete, 並且 後者分 別呼叫 allocator 的 Alloc() 和 Free().沒用 到 #01 #02 #03 #04 #05 #06 #07 #08 #09 #10 #11 #12 #13 #14 #15 #16 #17 #18 #19 #20 #21 #22 #23 #24 #25 http://jjhou.csdn.net jjhou.928@gmail.com 119 MFC CString 使用使用使用使用 CFixedAlloc CString str1("Hello");CString str1("Hello"); CString::CString(LPCTSTR lpsz) { Init(); // if (lpsz != NULL && HIWORD(lpsz) == NULL) {...} else { int nLen = SafeStrlen(lpsz); //本例 為 5 if (nLen != 0) { AllocBuffer(nLen); //㆘頁 memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } } } CString::CString(LPCTSTR lpsz) { Init(); // if (lpsz != NULL && HIWORD(lpsz) == NULL) {...} else { int nLen = SafeStrlen(lpsz); //本例 為 5 if (nLen != 0) { AllocBuffer(nLen); //㆘頁 memcpy(m_pchData, lpsz, nLen*sizeof(TCHAR)); } } } #define ROUND(x,y) (((x)+(y-1))&~(y-1)) #define ROUND4(x) ROUND(x, 4) AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData))); AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData))); AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData))); AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData))); #define ROUND(x,y) (((x)+(y-1))&~(y-1)) #define ROUND4(x) ROUND(x, 4) AFX_STATIC CFixedAlloc _afxAlloc64(ROUND4(65*sizeof(TCHAR)+sizeof(CStringData))); AFX_STATIC CFixedAlloc _afxAlloc128(ROUND4(129*sizeof(TCHAR)+sizeof(CStringData))); AFX_STATIC CFixedAlloc _afxAlloc256(ROUND4(257*sizeof(TCHAR)+sizeof(CStringData))); AFX_STATIC CFixedAlloc _afxAlloc512(ROUND4(513*sizeof(TCHAR)+sizeof(CStringData))); ㆘頁 用到 見 msdev\vc98\mfc\src\fixalloc.h, fixalloc.cpp 或 memory-management-in-cpp.pp 其 ctor 的 第㆓參 數預設 值是64,表示每次配 置 64 個區 塊。 第㆒ 參數表 示區塊 大小。 本例每 區塊大 小是65個字元+字串頭,並調 整至4倍數 nRefs, nDataLength, nAllocLength struct CStringData { long nRefs; //reference count int nDataLength; //length of data (including terminator) int nAllocLength; //length of allocation //TCHAR data[nAllocLength] TCHAR* data() //TCHAR* to managed data { return (TCHAR*)(this+1); } }; struct CStringData { long nRefs; //reference count int nDataLength; //length of data (including terminator) int nAllocLength; //length of allocation //TCHAR data[nAllocLength] TCHAR* data() //TCHAR* to managed data { return (TCHAR*)(this+1); } }; http://jjhou.csdn.net jjhou.928@gmail.com 120 void CString::AllocBuffer(int nLen) //always allocate one extra character for '\0' termination //assumes [optimistically] that data length will equal allocation length { if (nLen == 0) Init(); else { CStringData* pData; if (nLen <= 64) { pData = (CStringData*)_afxAlloc64.Alloc(); pData->nAllocLength=64; } else if (nLen <= 128) { pData = (CStringData*)_afxAlloc128.Alloc(); pData->nAllocLength = 128;} else if (nLen <= 256) { pData = (CStringData*)_afxAlloc256.Alloc(); pData->nAllocLength = 256;} else if (nLen <= 512) { pData = (CStringData*)_afxAlloc512.Alloc(); pData->nAllocLength = 512;} else { pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)]; pData->nAllocLength = nLen; } pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->nDataLength = nLen; m_pchData = pData->data(); } } void CString::AllocBuffer(int nLen) //always allocate one extra character for '\0' termination //assumes [optimistically] that data length will equal allocation length { if (nLen == 0) Init(); else { CStringData* pData; if (nLen <= 64) { pData = (CStringData*)_afxAlloc64.Alloc(); pData->nAllocLength=64; } else if (nLen <= 128) { pData = (CStringData*)_afxAlloc128.Alloc(); pData->nAllocLength = 128;} else if (nLen <= 256) { pData = (CStringData*)_afxAlloc256.Alloc(); pData->nAllocLength = 256;} else if (nLen <= 512) { pData = (CStringData*)_afxAlloc512.Alloc(); pData->nAllocLength = 512;} else { pData = (CStringData*)new BYTE[sizeof(CStringData) + (nLen+1)*sizeof(TCHAR)]; pData->nAllocLength = nLen; } pData->nRefs = 1; pData->data()[nLen] = '\0'; pData->nDataLength = nLen; m_pchData = pData->data(); } } H e l l o \01, 5, 64 64 (non-debug-version) ㆖頁 MFC CString 使用使用使用使用 CFixedAlloc http://jjhou.csdn.net jjhou.928@gmail.com 121 MFC CString 使用使用使用使用 CFixedAlloc 觀察前頁的 CString 源碼可知,CString 持有㆕個 (static) free-lists,分別處理字串實際 長度 <= 64, 128, 256, 512 ㆕種區塊,每個區塊大小因此分別是 65+sizeof(long+int+int)=77 129+sizeof(long+int+int)=141 257+sizeof(long+int+int)=269 513+sizeof(long+int+int)=525 這些區塊(所代表的字串)㆒旦被使用,即使釋放也是歸還給 CFixedAlloc 所管理的 ㆕個 free lists ㆗,並不會真正還給作業系統。事實㆖它們永遠不被還給作業系統。 http://jjhou.csdn.net jjhou.928@gmail.com 122 STL, Loki, MFC, Boost 的安裝的安裝的安裝的安裝 •STL 附隨編譯器而來。client 只需含入適當的 headers 即可 使用它。通常 client 使用 containers 而由後者使用 allocator。 client 絕少直接用㆖ allocator(但不是不可以,例如為 Foo 設計 operator new/delete 時就可直接在其內使用 allocator) •Loki 無需安裝,client 只需含入適當的 headers 即可使用它。 •MFC 需要安裝,client 除了含入適當的 headers 外還需安裝 好的 DLL 在 runtime 提供服務。 •Boost 需要安裝,以便產生若干 .dll 和 .lib。但若沒有安裝, 只要能含入適當的 headers 還是能正常使用某些 libraries(包 括 Boost.Pool) jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 1 malloc/free in VC6+ 侯捷侯捷侯捷侯捷 2009/11 • VC6 相關源碼相關源碼相關源碼相關源碼 • 觀察方式觀察方式觀察方式觀察方式 • malloc 行為剖析行為剖析行為剖析行為剖析 • free 行為剖析行為剖析行為剖析行為剖析 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 2 相關源碼相關源碼相關源碼相關源碼 in VC6 目錄檔名 主要 內容作用 c:\msdev\vc98\crt\srcwinheap.h BITVEC, LISTHEAD, ENTRY GROUP, REGION, HEADER c:\msdev\vc98\crt\srcsbheap.c __sbh_heap_init _heap_alloc_base, _free_base __sbh_alloc_block,__sbh_free_block __sbh_alloc_new_region __sbh_alloc_new_group Small-block heap code 定義 若干 consts 和 structs, needed by the C library heap code. c:\msdev\vc98\crt\srccrt0.c WinMainCRTStartup mainCRTStartupC runtime initialization routine c:\msdev\vc98\crt\srccrt0dat.c _cinit, exit, _exit32-bit C run-time initialization/termination routines c:\msdev\vc98\crt\srcheapinit.c _heap_init, _heap_term, Initialze the heap c:\msdev\vc98\crt\srcioinit.c _ioinitInitialization for lowio functions c:\msdev\vc98\crt\srcheap.h _GRANULARITY, _ROUND2 Contains information needed by the C library heap code. c:\msdev\vc98\crt\srcdbgheap.c malloc, freeDebug CRT Heap Functions sbh : small block heap jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 3 觀察方式觀察方式觀察方式觀察方式 in VC6 在 malloc(…) 呼叫式 ㆖設定 breakpoint,然 後 step into 追蹤 ㆘去。 對全局 大致理 解後, 在關鍵 處設立 breakpoints,觀 察 call stack 和 variables 內容 例如 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 4 #include #define BYTES_PER_PARA 16 #define DWORDS_PER_PARA 4 #define PARAS_PER_PAGE 256 // tunable value #define PAGES_PER_GROUP 8 // tunable value #define GROUPS_PER_REGION 32 // tunable value (max 32) #define BYTES_PER_PAGE (BYTES_PER_PARA * PARAS_PER_PAGE) #define BYTES_PER_GROUP (BYTES_PER_PAGE * PAGES_PER_GROUP) #define BYTES_PER_REGION (BYTES_PER_GROUP * GROUPS_PER_REGION) #define ENTRY_OFFSET 0x0000000cL // offset of entry in para #define OVERHEAD_PER_PAGE 0x00000010L // sixteen bytes of overhead #define MAX_FREE_ENTRY_SIZE (BYTES_PER_PAGE - OVERHEAD_PER_PAGE) #define BITV_COMMIT_INIT (((1 << GROUPS_PER_REGION) - 1) << \ (32 - GROUPS_PER_REGION)) #define MAX_ALLOC_DATA_SIZE 0x3f8 #define MAX_ALLOC_ENTRY_SIZE (MAX_ALLOC_DATA_SIZE + 0x8) #include #define BYTES_PER_PARA 16 #define DWORDS_PER_PARA 4 #define PARAS_PER_PAGE 256 // tunable value #define PAGES_PER_GROUP 8 // tunable value #define GROUPS_PER_REGION 32 // tunable value (max 32) #define BYTES_PER_PAGE (BYTES_PER_PARA * PARAS_PER_PAGE) #define BYTES_PER_GROUP (BYTES_PER_PAGE * PAGES_PER_GROUP) #define BYTES_PER_REGION (BYTES_PER_GROUP * GROUPS_PER_REGION) #define ENTRY_OFFSET 0x0000000cL // offset of entry in para #define OVERHEAD_PER_PAGE 0x00000010L // sixteen bytes of overhead #define MAX_FREE_ENTRY_SIZE (BYTES_PER_PAGE - OVERHEAD_PER_PAGE) #define BITV_COMMIT_INIT (((1 << GROUPS_PER_REGION) - 1) << \ (32 - GROUPS_PER_REGION)) #define MAX_ALLOC_DATA_SIZE 0x3f8 #define MAX_ALLOC_ENTRY_SIZE (MAX_ALLOC_DATA_SIZE + 0x8) Winheap.h 16 * 256 = 4096 4096 * 8 = 32768 32768 * 32 = 1048576 12 16 4096 - 16 = 4080 0xFFFFFFFF 1016 1016 + 8 = 1024 16 * 256 = 4096 4096 * 8 = 32768 32768 * 32 = 1048576 12 16 4096 - 16 = 4080 0xFFFFFFFF 1016 1016 + 8 = 1024 保留 0xFFFFFFFF page 4096 0xFFFFFFFF 4080 若干常數若干常數若干常數若干常數 每個 pages 有 4096 bytes 每個 group 有 8 pages 每個 region 有 32 group void * __cdecl _heap_alloc_base (size_t size) { void * pvReturn; if (size <= __sbh_threshold) //3F8, i.e. 1016 { pvReturn = __sbh_alloc_block(size); return pvReturn; } ... return HeapAlloc(_crtheap, 0, size); } jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 5 malloc() in VC6 typedef unsigned int BITVEC; typedef struct tagListHead { struct tagEntry * pEntryNext; struct tagEntry * pEntryPrev; } LISTHEAD, *PLISTHEAD; typedef struct tagEntry { int sizeFront; struct tagEntry * pEntryNext; struct tagEntry * pEntryPrev; } ENTRY, *PENTRY; typedef struct tagEntryEnd { int sizeBack; } ENTRYEND, *PENTRYEND; typedef struct tagGroup { int cntEntries; struct tagListHead listHead[64]; } GROUP, *PGROUP; typedef struct tagRegion { int indGroupUse; char cntRegionSize[64]; BITVEC bitvGroupHi[GROUPS_PER_REGION]; BITVEC bitvGroupLo[GROUPS_PER_REGION]; struct tagGroup grpHeadList[GROUPS_PER_REGION]; } REGION, *PREGION; typedef unsigned int BITVEC; typedef struct tagListHead { struct tagEntry * pEntryNext; struct tagEntry * pEntryPrev; } LISTHEAD, *PLISTHEAD; typedef struct tagEntry { int sizeFront; struct tagEntry * pEntryNext; struct tagEntry * pEntryPrev; } ENTRY, *PENTRY; typedef struct tagEntryEnd { int sizeBack; } ENTRYEND, *PENTRYEND; typedef struct tagGroup { int cntEntries; struct tagListHead listHead[64]; } GROUP, *PGROUP; typedef struct tagRegion { int indGroupUse; char cntRegionSize[64]; BITVEC bitvGroupHi[GROUPS_PER_REGION]; BITVEC bitvGroupLo[GROUPS_PER_REGION]; struct tagGroup grpHeadList[GROUPS_PER_REGION]; } REGION, *PREGION; typedef struct tagHeader { BITVEC bitvEntryHi; BITVEC bitvEntryLo; BITVEC bitvCommit; void * pHeapData; struct tagRegion * pRegion; } HEADER, *PHEADER; extern HANDLE _crtheap; /* * Global variable declarations for the small-block heap. */ extern size_t __sbh_threshold; extern PHEADER __sbh_pHeaderList; // pointer to list start extern PHEADER __sbh_pHeaderScan; // pointer to list rover extern int __sbh_sizeHeaderList; // allocated size of list extern int __sbh_cntHeaderList; // count of entries defined extern PHEADER __sbh_pHeaderDefer; extern int __sbh_indGroupDefer; ... typedef struct tagHeader { BITVEC bitvEntryHi; BITVEC bitvEntryLo; BITVEC bitvCommit; void * pHeapData; struct tagRegion * pRegion; } HEADER, *PHEADER; extern HANDLE _crtheap; /* * Global variable declarations for the small-block heap. */ extern size_t __sbh_threshold; extern PHEADER __sbh_pHeaderList; // pointer to list start extern PHEADER __sbh_pHeaderScan; // pointer to list rover extern int __sbh_sizeHeaderList; // allocated size of list extern int __sbh_cntHeaderList; // count of entries defined extern PHEADER __sbh_pHeaderDefer; extern int __sbh_indGroupDefer; ... 2 3 sbheap.c size_t __sbh_threshold = MAX_ALLOC_DATA_SIZE; PHEADER __sbh_pHeaderList; // pointer to list start PHEADER __sbh_pHeaderScan; // pointer to list rove int __sbh_sizeHeaderList; // allocated size of list int __sbh_cntHeaderList; // count of entries defined PHEADER __sbh_pHeaderDefer; int __sbh_indGroupDefer; size_t __sbh_threshold = MAX_ALLOC_DATA_SIZE; PHEADER __sbh_pHeaderList; // pointer to list start PHEADER __sbh_pHeaderScan; // pointer to list rover int __sbh_sizeHeaderList; // allocated size of list int __sbh_cntHeaderList; // count of entries defined PHEADER __sbh_pHeaderDefer; int __sbh_indGroupDefer; 若干結構若干結構若干結構若干結構 ㆖頁, 1016(3f8h) 大小: 8 bytes 大小: 12 bytes 大小: 4 bytes 大小: 4+8*64 bytes 大小: 4+1*64+4*32+4*32 bytes 大小: 4+4+4+4+4 = 20(14h) bytes 32 1 4 5 Winheap.h Winheap.h jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 6 /* * For diagnostic purpose, blocks are allocated with extra information a * stored in a doubly-linked list. This makes all blocks registered with * how big they are, when they were allocated, and what they are used f */ #define nNoMansLandSize 4 typedef struct _CrtMemBlockHeader { struct _CrtMemBlockHeader * pBlockHeaderNext; struct _CrtMemBlockHeader * pBlockHeaderPrev; char * szFileName; int nLine; size_t nDataSize; int nBlockUse; long lRequest; unsigned char gap[nNoMansLandSize]; /* followed by: * unsigned char data[nDataSize]; * unsigned char anotherGap[nNoMansLandSize]; */ } _CrtMemBlockHeader; /* * For diagnostic purpose, blocks are allocated with extra information an * stored in a doubly-linked list. This makes all blocks registered with * how big they are, when they were allocated, and what they are used f */ #define nNoMansLandSize 4 typedef struct _CrtMemBlockHeader { struct _CrtMemBlockHeader * pBlockHeaderNext; struct _CrtMemBlockHeader * pBlockHeaderPrev; char * szFileName; int nLine; size_t nDataSize; int nBlockUse; long lRequest; unsigned char gap[nNoMansLandSize]; /* followed by: * unsigned char data[nDataSize]; * unsigned char anotherGap[nNoMansLandSize]; */ } _CrtMemBlockHeader; main() _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() KERNEL32! bff8b6e6() KERNEL32! bff8b598() KERNEL32! bff89f5b() main() _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() KERNEL32! bff8b6e6() KERNEL32! bff8b598() KERNEL32! bff89f5b() 進入 main() 之前 ,有這 樣的 call stack : 要求 40h 要求 64h 要求 64h 膨脹24h. 索求量會被膨脹索求量會被膨脹索求量會被膨脹索求量會被膨脹 36(24h)bytes blockSize = sizeof(_CrtMemBlockHeader) + nSize + nNoMansLandSize; =32+64+4 #ifndef WINHEAP /* round requested size */ blockSize = _ROUND2(blockSize, _GRANULARITY); #endif /* WINHEAP */ 4+4+4+4+4+4+4+ = 32 (20h) char* int size_t int long char[4] jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 7 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... #6 group 4096 (扣掉overhead是4080) 每次添加16個Headers,用 光了再配置16個…每個 Header 表現㆒個 Region. region 內含 32 groups 每個 group 有 8 pages 每單元 (group) 8 個 pages 虛 擬 位 址 空 間 32 個Groups 64 個 ListHeads 表現 64 條 freelists, 分別維護 16, 32, 48, 64…1008 bytes 等特定區塊. 最後㆒個 list 則維護大額區塊 (4080 > size > 1009 ) pRegion pHeapData indGroupUse __sbh_sizeHeaderList __sbh_cntHeaderList … ... __sbh_pHeaderList #n 組 64 bits 負責記錄 #n Group 的 64 個 entries 的情況 #n 元素記錄 #n 縱列之 bits 'or'。 bit 值為1 之個數 (最多 32) 被記錄於cntRegionSize[n]. #n 元素若為1,表示 32 個 groups ㆗的至少㆒個的 #n ListHead 掛著可用區塊. 0表示對應之group 堪用 詳情見㆘頁 group 6 總是對應 #6 單元 group n 總是對應 #n 單元 cntRegionSize[64] 目前 使用的 group 編號. 這 些 資 料 用 來 控 制 實 際 region (1MB) 的 使 用 region VirtualAlloc( ,1M,MEM_RESERVE, ) VirtualAlloc( ,32K, MEM_COMMIT, ) HeapAlloc(,,16*sizeof(HEADER)) HeapAlloc(,,sizeof(REGION)); jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 8 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 #1完成完成完成完成 __sbh_alloc_block(124h) 後的 結果後的 結果後的 結果後的 結果 return 7d0ed0; 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d7000 007d0000 8bytes 保留 0xffffffff 0x00000ec0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 ... ... ... ... ... ... 1... ... 64 chars 64 bits,共 32 組 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 if (indGroupUse == -1 || // determine the group to allocate from !((bitvEntryHi & pRegion->bitvGroupHi[indGroupUse]) | (bitvEntryLo & pRegion->bitvGroupLo[indGroupUse]))) 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 1 ... ... 1 1 1 1 1 1 1 1 1 1 124h / 292 => 130h / 304 (#18) => 00003FFF FFFFFFFF pEntry = pGroup->listHead[63].pEntryNext; 007d000c 007d100c 006c03b4 006c03b4 1 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d000c 006c03b4 007d700c 007d0ed0 007d1000 007d0ecc 0x00000131 0x00000ec0 0x00000131 007d0ff8 0xffffffff ec0 =ff0-130 007d0ec8 ec0 130 add 8 bytes entry overhead and round up to next para size 0-63 個 ListHeads 表現 64 條 freelists #3fh 置 1 #18 bit (含)後皆為1 表示高於#18(含)的 lists 若掛有 block(s) 皆可取用 綠色㆖面那㆒塊(內容131) 就是cookie. 唯需注意malloc 所 得指標不是這兒的 7d0ed0,因 為還存在 extra info. for debug. jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 9 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 #2完成完成完成完成 __sbh_alloc_block(230h) 後的 結果後的 結果後的 結果後的 結果 return 7d0c90; 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d7000 007d0000 8bytes 保留 0xffffffff 0x00000c80 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 ... ... ... ... ... ... 1... ... 64 chars 64 bits,共 32 組 pEntry = pGroup->listHead[63].pEntryNext; 007d000c 007d100c 006c03b4 006c03b4 2 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d000c 006c03b4 007d700c 007d0c90 007d1000 007d0c8c 0x00000241 0x00000c80 0x00000241 007d0ec8 0xffffffff c80 =ec0-240 230h/560 => 240h/576 (#35) => 00000000 1FFFFFFF 007d0c88 0x00000131 0x00000131 007d0ecc c80 130 240 add 8 bytes entry overhead and round up to next para size 00000000 00000001 0-63 個 ListHeads 表現 64 條 freelists #3fh 置 1 00000000 1FFFFFFF (ori.)00000000 00000001 #35 bit (含)後皆為1 表示高於#35(含)的 lists 若掛有 block(s) 皆可取用 表示目前 #63 list 掛有 block(s) 可用 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 10 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 #3完成完成完成完成 __sbh_alloc_block(64h) 後的 結果後的 結果後的 結果後的 結果 return 7d0c20; 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x00000c10 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 ... ... ... ... ... ... 1... ... 64 chars 64 bits,共 32 組 pEntry = pGroup->listHead[63].pEntryNext; 007d000c 007d100c 006c03b4 006c03b4 3 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d000c 006c03b4 007d700c 007d0c20 007d1000 007d0c1c 0x00000071 0x00000c10 0x00000071 007d0c88 c10 =c80-70 64h / 100 => 70h / 112 (#6) => 03FFFFFF FFFFFFFF 007d0c18 007d0c8c 0x00000241 0x00000241 0xffffffff 0x00000131 0x00000131 240 70 130 c10 add 8 bytes entry overhead and round up to next para size 00000000 00000001 0-63 個 ListHeads 表現 64 條 freelists #3fh 置 1 03FFFFFF FFFFFFFF (ori.)00000000 00000001 #6 bit (含)後皆為1 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 11 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 #14完成完成完成完成 __sbh_alloc_block(62h) 後的 結果後的 結果後的 結果後的 結果 return 7d07d0; 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000007c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 ... ... ... ... ... ... 1... ... 64 chars 64 bits,共 32 組 pEntry = pGroup->listHead[63].pEntryNext; 007d000c 007d100c 006c03b4 006c03b4 e 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d000c 006c03b4 007d700c 007d07d0 007d07cc 0x000007c0 007d0838 7c0 =830-70 62h / 98 => 70h / 112 (#6) => 03FFFFFF FFFFFFFF 007d07c8 007d083c 7c0 0xffffffff 0x00000041 0x00000041 40 0x00000041 0x00000041 40 0x00000111 0x00000111 110 0x00000071 0x00000071 70 007d1000 add 8 bytes entry overhead and round up to next para size 00000000 00000001 0-63 個 ListHeads 表現 64 條 freelists #3fh 置 1 03FFFFFF FFFFFFFF (ori.)00000000 00000001 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 12 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups 0-63 個 ListHeads 表現 64 條 freelists pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000007c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 ... ... ... ... ... ... 1... ... 64 chars 64 bits,共 32 組 pEntry = pGroup->listHead[63].pEntryNext; 007d000c 007d100c 006c03b4 006c03b4 d 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d000c 006c03b4 007d700c 007d0bc0 007d1000 007d0bbc 0x00000061 0x00000061 007d0c18 007d0c1c 0x00000240 0x00000240 0xffffffff 0x00000131 0x00000131 240 60 130 0x00000071 0x00000071 70 #14'完成完成完成完成 _free_base(7d0c90); ÎÎÎÎ __sbh_free_block(7c000c,7d0c90) 後的 結果後的 結果後的 結果後的 結果 007d0c90 007d0ed0 00000000 10000001 這㆒塊(大小240h)回收後應由 #35 headOfList 管理 (240h=576, 576/16=36) 0000000010000001 #35 #34 #23,#3fh 置 1 006c02d4 7d0c90是#2配得的記憶體 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 13 006c027c = 006c01bc+4+24*8-4 = 006c01bc + 192 = 006c01bc + c0 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 #15完成完成完成完成 __sbh_alloc_block(a4h) 後的 結果後的 結果後的 結果後的 結果 return 7d0e20; 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000007c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 ... ... ... ... ... ... 1... ... 64 chars 64 bits,共 32 組 pEntry = pGroup->listHead[63].pEntryNext; 007d000c 007d100c 006c03b4 006c03b4 e 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d000c 006c03b4 007d700c 007d0e20 007d1000 007d0e1c 0x000000b1 0x00000190 0x000000b1 007d0ec8 0xffffffff 007d0e18 0x00000131 0x00000131 007d0ecc 130 b0 a4h / 164 => b0h / 176 (#10) => 003FFFFF FFFFFFFF 0x00000190 190 0x00000071 0x00000071 0x00000061007d0c1c 70 0x000007c0 7c0 007d07c8 240 add 8 bytes entry overhead and round up to next para size 00000080 00000001#18h, #3fh 置 1 0-63 個 ListHeads 表現 64 條 freelists #24 #23 006c027c 007d0c8c 0000008000000001 剩餘190h/400 所以改掛 #24 003FFFFF FFFFFFFF (ori.)00000000 10000001 用完之後變小了,所以改串至: jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 14 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000001c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 ... ... ... ... ... ... 1 1... ... 64 chars 64 bits,共 32 組 007d100c 006c0294 006c0294 006c03b4 18 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d200c 006c03b4 007d700c 0xffffffff 1c0 =4c0-300 1c0 #26完成完成完成完成 malloc(2cch) ÎÎÎÎ __sbh_alloc_block(2f0h) 後的 結果後的 結果後的 結果後的 結果return 7d11d0 0-63 個 ListHeads 表現 64 條 freelists return 7d11f0 add 8 bytes entry overhead and round up to next para size 02000014 00000001 0x00000231 0x00000231 230 0x000001c0 007d1dcc 007d1ffc 00000000 0001FFFF (ori.)02000004 00000001 用完之後變小了,改串至#27(因1c0h/448 隸屬#27): 2f0h/752 => 300h/768(#47) => 00000000 0001FFFF 0x00000301 0x00000301 300 007d1acc 0x00000301 0x00000301 300 007d17cc 0x00000301 0x00000301 300 007d14cc 0x00000301 0x00000301 300 007d11cc p9 p8 p7 p6 p5 #27 006c0294 #6h,#1bh,#1dh,#3fh 置 1 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 15 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000001c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 ... ... ... ... ... ... 1 1... ... 64 chars 64 bits,共 32 組 007d200c 006c03b4 1d 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d300c 006c03b4 007d700c 0xffffffff 1c0 =4c0-300 1c0 接㆘ 來接㆘ 來接㆘ 來接㆘ 來 malloc(200h),malloc(2cch),malloc(2cch), malloc(2cch),malloc(2cch), 結果 :結果 :結果 :結果 : 0-63 個 ListHeads 表現 64 條 freelists 02000014 00000001 0x00000231 0x00000231 230 0x000001c0 007d2dcc 007d2ffc 0x00000301 0x00000301 300 007d2acc 0x00000301 0x00000301 300 007d27cc 0x00000301 0x00000301 300 007d24cc 0x00000301 0x00000301 300 007d21cc #27 006c0294 #6h,#1bh,#1dh,#3fh 置 1 0x000001c0 007d100c 此塊 見㆖頁 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 16 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000001c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 ... ... ... ... ... ... 1 1... ... 64 chars 64 bits,共 32 組 007d300c 006c03b4 22 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d400c 006c03b4 007d700c 0xffffffff 1c0 =4c0-300 1c0 再次 完成再次 完成再次 完成再次 完成 malloc(200h),malloc(2cch),malloc(2cch), malloc(2cch),malloc(2cch), 結果 :結果 :結果 :結果 : 0-63 個 ListHeads 表現 64 條 freelists 02000014 00000001 0x00000231 0x00000231 230 0x000001c0 007d3dcc 007d3ffc 0x00000301 0x00000301 300 007d3acc 0x00000301 0x00000301 300 007d37cc 0x00000301 0x00000301 300 007d34cc 0x00000301 0x00000301 300 007d31cc #27 006c0294 #6h,#1bh,#1dh,#3fh 置 1 0x000001c0 007d100c 0x000001c0 007d200c 此塊 見㆖頁 此塊 見㆖㆖ 頁 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 17 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000001c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 ... ... ... ... ... ... 1 1... ... 64 chars 64 bits,共 32 組 007d600c 006c03b4 31 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 007d700c 006c03b4 007d700c 0xffffffff 1c0 =4c0-300 1c0 再㆔ 次再㆔ 次再㆔ 次再㆔ 次 malloc(200h),malloc(2cch),malloc(2cch), malloc(2cch),malloc(2cch), 結果 :結果 :結果 :結果 : 0-63 個 ListHeads 表現 64 條 freelists 02000014 00000001 0x00000231 0x00000231 230 0x000001c0 007d6dcc 007d6ffc 0x00000301 0x00000301 300 007d6acc 0x00000301 0x00000301 300 007d67cc 0x00000301 0x00000301 300 007d64cc 0x00000301 0x00000301 300 007d61cc #27 006c0294 #6h,#1bh,#1dh,#3fh 置 1 0x000001c0 007d100c 0x000001c0 007d200c ... 006c03b4 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 18 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=0 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000001c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 1 0 0 ... ... ... ... ... ... 1 0... ... 64 chars 64 bits,共 32 組 007d700c 006c03b4 36 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 0xffffffff 1c0 =4c0-300 1c0 最後 ,再次最後 ,再次最後 ,再次最後 ,再次 malloc(200h),malloc(2cch),malloc(2cch), malloc(2cch),malloc(2cch), 結果 :結果 :結果 :結果 :注意#63 ListHead 0-63 個 ListHeads 表現 64 條 freelists 02000014 00000000 0x00000231 0x00000231 230 0x000001c0 007d7dcc 007d7ffc 0x00000301 0x00000301 300 007d7acc 0x00000301 0x00000301 300 007d77cc 0x00000301 0x00000301 300 007d74cc 0x00000301 0x00000301 300 007d71cc #27 006c0294 #6h,#1bh,#1dh 置 1 0x000001c0 007d100c 0x000001c0 007d200c ... jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 19 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=1 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 0x006c03c0 0x007dF000 0x007d8000 8bytes 保留 0xffffffff 0ff0(4080) 0ff0(4080) 0xffffffff 每單元實景 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 1 0 0 00 00 00 00 00 00 00 00 1... ... ... ... ... ... 1 1... ... 64 chars 64 bits,共 32 組 這表示 #0, #1 group 堪用. 007d800c 007d9000 007d8ff8 ff0 0-63 個 ListHeads 表現 64 條 freelists 再再再再 malloc(200h), 於是喚 起於是喚 起於是喚 起於是喚 起 __sbh_alloc_new_group(), 執行 完畢後 :執行 完畢後 :執行 完畢後 :執行 完畢後 : 02000014 00000000 00000000 00000001 02000014 00000001 兩者聯集 01 #6h,#1bh,#1dh,#3fh 置 1 注意,用㆖第㆓個 8 pages 和第㆓個 group jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 20 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=1 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000001c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 ... ... ... ... ... ... 1 1... ... 64 chars 64 bits,共 32 組 007d100c 007d200c 006c03b4 006c03b4 35 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 0xffffffff 1c0 =4c0-300 1c0 #27完成完成完成完成 free(p6) ÎÎÎÎ __sbh_free_block(7c000ch, 7d1ad0h) 後的結果後的結果後的結果後的結果 0-63 個 ListHeads 表現 64 條 freelists 7d1af0 0x00000231 0x00000231 230 0x000001c0 007d1dcc 007d1ffc 0x00000300 0x00000300 300 0x00000301 0x00000301 300 007d17cc 0x00000301 0x00000301 300 007d14cc 0x00000301 0x00000301 300 007d11cc p9 p8 p7 p6 p5 300h/768(#47) => 00000000 0001FFFF 釋放大小是 300h, 應由#47掌管 #47 006c0334 007d1acc #6h,#1bh,#1dh,#2f, #3fh 置 1 02000014 00010001 0 1 1 02000014 00010000 00000000 00000001 兩者聯集而得 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 21 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=1 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000001c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 1 0 0 0 1... ... ... ... ... ... 1 1... ... 64 chars 64 bits,共 32 組 007d100c 007d200c 006c03b4 006c03b4 34 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 0xffffffff 1c0 =4c0-300 1c0 #28完成完成完成完成 free(p8) ÎÎÎÎ __sbh_free_block(7c000ch, 7d14d0h) 後的結果後的結果後的結果後的結果 0-63 個 ListHeads 表現 64 條 freelists 7d14f0 0x00000231 0x00000231 230 0x000001c0 007d1dcc 007d1ffc 0x00000300 0x00000300 300 007d1acc 0x00000301 0x00000301 300 007d17cc 0x00000300 0x00000300 300 007d14cc 0x00000301 0x00000301 300 007d11cc p9 p8 p7 p6 p5 300h/768(#47) => 00000000 0001FFFF #47 006c0334 #6h,#1bh,#1dh,#2f, #3fh 置 1 我還以為 #47 的大小會記錄 2 (不會,這和最初 #63 鏈了8 個 entries 類似) 釋放大小是 300h, 應由#47掌管 02000014 00010001 02000014 00010000 00000000 00000001 兩者聯集而得 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 22 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=1 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 006c01bc 007d0000 8bytes 保留 0xffffffff 0x000001c0 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 1... ... ... ... ... ... 1 2... ... 64 chars 64 bits,共 32 組 007d100c 007d200c 006c03b4 006c03b4 33 006c01bc 006c01c4 006c01c4 006c01cc 006c01cc 006c03ac 0xffffffff 1c0 =4c0-300 1c0 #29完成完成完成完成 free(p7) ÎÎÎÎ __sbh_free_block(7c000ch, 7d17d0h) 後的結果後的結果後的結果後的結果 0-63 個 ListHeads 表現 64 條 freelists 7d17f0 0x00000231 0x00000231 230 0x000001c0 007d1dcc 007d1ffc 0x00000900 007d1acc 900 007d17cc 合併 0x00000900 007d14cc 0x00000301 0x00000301 300 007d11cc p9 p8 p7 p6 p5 300h/768(#47) => 00000000 0001FFFF #6h,#1bh,#1dh 置 1, #3fh 置 2 釋放大小是 300h, 應由#47掌管 02000014 00000001 02000014 00000001 00000000 00000001 兩者聯集. cntRegionSize 記錄重複次數. 最多 32 個重複. char 足矣 #0, #1groups 的 #63 head 都 可供應 區塊, 那麼 當接㆘ 來 malloc(0x3C0) 誰先 被選呢 ?Ans: #1 group jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 18 #001 void __cdecl __sbh_free_block (PHEADER pHeader, void * pvAlloc) #002 { #003 PREGION pRegion; #004 PGROUP pGroup; #005 PENTRY pHead; #006 PENTRY pEntry; #007 PENTRY pNext; #008 PENTRY pPrev; #009 void * pHeapDecommit; #010 int sizeEntry; #011 int sizeNext; #012 int sizePrev; #013 unsigned int indGroup; #014 unsigned int indEntry; #015 unsigned int indNext; #016 unsigned int indPrev; #017 unsigned int offRegion; #018 #019 // region is determined by the header #020 pRegion = pHeader->pRegion; #021 #022 // use the region offset to determine the group index #023 offRegion = (unsigned int)pvAlloc - (unsigned int)pHeader->pHeapData; #024 indGroup = offRegion / BYTES_PER_GROUP; #025 pGroup = &pRegion->grpHeadList[indGroup]; #026 #027 // get size of entry - decrement value since entry is allocated #028 pEntry = (PENTRY)((char *)pvAlloc - sizeof(int)); #029 sizeEntry = pEntry->sizeFront - 1; #030 #031 // point to next entry to get its size #032 pNext = (PENTRY)((char *)pEntry + sizeEntry); #033 sizeNext = pNext->sizeFront; #034 #035 // get size from end of previous entry #036 sizePrev = ((PENTRYEND)((char *)pEntry - sizeof(int)))->sizeBack; #001 void __cdecl __sbh_free_block (PHEADER pHeader, void * pvAlloc) #002 { #003 PREGION pRegion; #004 PGROUP pGroup; #005 PENTRY pHead; #006 PENTRY pEntry; #007 PENTRY pNext; #008 PENTRY pPrev; #009 void * pHeapDecommit; #010 int sizeEntry; #011 int sizeNext; #012 int sizePrev; #013 unsigned int indGroup; #014 unsigned int indEntry; #015 unsigned int indNext; #016 unsigned int indPrev; #017 unsigned int offRegion; #018 #019 // region is determined by the header #020 pRegion = pHeader->pRegion; #021 #022 // use the region offset to determine the group index #023 offRegion = (unsigned int)pvAlloc - (unsigned int)pHeader->pHeapData; #024 indGroup = offRegion / BYTES_PER_GROUP; #025 pGroup = &pRegion->grpHeadList[indGroup]; #026 #027 // get size of entry - decrement value since entry is allocated #028 pEntry = (PENTRY)((char *)pvAlloc - sizeof(int)); #029 sizeEntry = pEntry->sizeFront - 1; #030 #031 // point to next entry to get its size #032 pNext = (PENTRY)((char *)pEntry + sizeEntry); #033 sizeNext = pNext->sizeFront; #034 #035 // get size from end of previous entry #036 sizePrev = ((PENTRYEND)((char *)pEntry - sizeof(int)))->sizeBack; 0x007c000c, 0x007d17d0 0x000017d0 0x00000000 0x006c01bc 0x007d17cc 0x00000300 0x007d1acc 0x00000300 0x00000300 計算 ㆖方區 塊大小 計算 ㆘方區 塊大小 計算 本區塊 大小 從這 兩個指 標就可 以算出 其距離 , 從而 算出後 個指標 落於 #n group。 (因 為每個 group 負責 8 pages) 8bytes 保留 0xffffffff 0x000001c0 007d100c 007d200c 006c03b4 0xffffffff 1c0 0x00000231 0x00000231 230 0x000001c0 007d1dcc 007d1ffc 0x00000300 0x00000300 300 007d1acc 0x00000301 0x00000301 300 007d17cc 0x00000300 0x00000300 300 007d14cc 0x00000301 0x00000301 300 007d11cc p9 p8 p7 p6 p5 歸 還 與 合 併 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 19 #038 // test if next entry is free by an even size value #039 #040 if ((sizeNext & 1) == 0) // if ((0x300 & 1) == 0) #041 { #042 // free next entry - disconnect and add its size to sizeEntry #043 #044 // determine index of next entry #045 indNext = (sizeNext >> 4) - 1; // = (300 >> 4) - 1; 得 2Fh #046 if (indNext > 63) #047 indNext = 63; #048 #049 // test entry is sole member of bucket (next == prev), #050 if (pNext->pEntryNext == pNext->pEntryPrev) #051 { #052 // clear bit in group vector, decrement region count #053 // if region count is now zero, clear bit in header #054 // entry vector #055 if (indNext < 32) #056 { #057 pRegion->bitvGroupHi[indGroup] &= ~(0x80000000L >> indNext); #058 if (--pRegion->cntRegionSize[indNext] == 0) #059 pHeader->bitvEntryHi &= ~(0x80000000L >> indNext); #060 } #061 else #062 { #063 pRegion->bitvGroupLo[indGroup] &= #064 ~(0x80000000L >> (indNext - 32)); #065 if (--pRegion->cntRegionSize[indNext] == 0) #066 pHeader->bitvEntryLo &= ~(0x80000000L >> (indNext - 32)); #067 } #068 } #038 // test if next entry is free by an even size value #039 #040 if ((sizeNext & 1) == 0) // if ((0x300 & 1) == 0) #041 { #042 // free next entry - disconnect and add its size to sizeEntry #043 #044 // determine index of next entry #045 indNext = (sizeNext >> 4) - 1; // = (300 >> 4) - 1; 得 2Fh #046 if (indNext > 63) #047 indNext = 63; #048 #049 // test entry is sole member of bucket (next == prev), #050 if (pNext->pEntryNext == pNext->pEntryPrev) #051 { #052 // clear bit in group vector, decrement region count #053 // if region count is now zero, clear bit in header #054 // entry vector #055 if (indNext < 32) #056 { #057 pRegion->bitvGroupHi[indGroup] &= ~(0x80000000L >> indNext); #058 if (--pRegion->cntRegionSize[indNext] == 0) #059 pHeader->bitvEntryHi &= ~(0x80000000L >> indNext); #060 } #061 else #062 { #063 pRegion->bitvGroupLo[indGroup] &= #064 ~(0x80000000L >> (indNext - 32)); #065 if (--pRegion->cntRegionSize[indNext] == 0) #066 pHeader->bitvEntryLo &= ~(0x80000000L >> (indNext - 32)); #067 } #068 } ㆘方區塊若為 free, 合併之(unlink ㆘方區塊) #069 #070 // unlink entry from list #071 pNext->pEntryPrev->pEntryNext = pNext->pEntryNext; #072 pNext->pEntryNext->pEntryPrev = pNext->pEntryPrev; #073 #074 // add next entry size to freed entry size #075 sizeEntry += sizeNext; // lhs 得 600h #076 } #069 #070 // unlink entry from list #071 pNext->pEntryPrev->pEntryNext = pNext->pEntryNext; #072 pNext->pEntryNext->pEntryPrev = pNext->pEntryPrev; #073 #074 // add next entry size to freed entry size #075 sizeEntry += sizeNext; // lhs 得 600h #076 } 繞過 pNext(亦 即打斷 關係) 8bytes 保留 0xffffffff 0x000001c0 007d100c 007d200c 006c03b4 0xffffffff 1c0 0x00000231 0x00000231 230 0x000001c0 007d1dcc 007d1ffc 0x00000300 0x00000300 300 007d1acc 0x00000301 0x00000301 300 007d17cc 0x00000300 0x00000300 300 007d14cc 0x00000301 0x00000301 300 007d11cc 8bytes 保留 0xffffffff 0x000001c0 007d100c 007d200c 006c03b4 0xffffffff 1c0 0x00000231 0x00000231 230 0x000001c0 007d1dcc 007d1ffc 600合併 007d17cc 0x00000300 0x00000300 300 007d14cc 0x00000301 0x00000301 300 007d11cc jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 20 #078 // compute index of free entry (plus next entry if it was free) #079 indEntry = (sizeEntry >> 4) - 1; // =(600 >> 4) -1 得 0x5F #080 if (indEntry > 63) #081 indEntry = 63; #082 #083 // test if previous entry is free by an even size value #084 if ((sizePrev & 1) == 0) // if ((0x300 & 1) == 0) #085 { #086 // free previous entry - add size to sizeEntry and #087 // disconnect if index changes #088 #089 // get pointer to previous entry #090 pPrev = (PENTRY)((char *)pEntry - sizePrev); #091 #092 // determine index of previous entry #093 indPrev = (sizePrev >> 4) - 1; // = (300h >> 4) -1 得 2Fh #094 if (indPrev > 63) #095 indPrev = 63; #096 #097 // add previous entry size to sizeEntry and determine #098 // its new index #099 sizeEntry += sizePrev; //得900h #100 indEntry = (sizeEntry >> 4) - 1; //得 8Fh #101 if (indEntry > 63) #102 indEntry = 63; #103 #078 // compute index of free entry (plus next entry if it was free) #079 indEntry = (sizeEntry >> 4) - 1; // =(600 >> 4) -1 得 0x5F #080 if (indEntry > 63) #081 indEntry = 63; #082 #083 // test if previous entry is free by an even size value #084 if ((sizePrev & 1) == 0) // if ((0x300 & 1) == 0) #085 { #086 // free previous entry - add size to sizeEntry and #087 // disconnect if index changes #088 #089 // get pointer to previous entry #090 pPrev = (PENTRY)((char *)pEntry - sizePrev); #091 #092 // determine index of previous entry #093 indPrev = (sizePrev >> 4) - 1; // = (300h >> 4) -1 得 2Fh #094 if (indPrev > 63) #095 indPrev = 63; #096 #097 // add previous entry size to sizeEntry and determine #098 // its new index #099 sizeEntry += sizePrev; //得900h #100 indEntry = (sizeEntry >> 4) - 1; //得 8Fh #101 if (indEntry > 63) #102 indEntry = 63; #103 #104 // if index changed due to coalesing, reconnect to new size #105 if (indPrev != indEntry) // if (2Fh != 8Fh) #106 { #107 // disconnect entry from indPrev #108 // test entry is sole member of bucket (next == prev), #109 if (pPrev->pEntryNext == pPrev->pEntryPrev) #110 { #111 // clear bit in group vector, decrement region count #112 // if region count is now zero, clear bit in header #113 // entry vector #114 if (indPrev < 32) // if (2Fh < 32) #115 { #116 pRegion->bitvGroupHi[indGroup] &= #117 ~(0x80000000L >> indPrev); #118 if (--pRegion->cntRegionSize[indPrev] == 0) #119 pHeader->bitvEntryHi &= ~(0x80000000L >> indPrev); #120 } #121 else #122 { #123 pRegion->bitvGroupLo[indGroup] &= //0xfffeffff #124 ~(0x80000000L >> (indPrev - 32)); #125 if (--pRegion->cntRegionSize[indPrev] == 0) //if (--1 == 0) #126 pHeader->bitvEntryLo &= //0xfffeffff #127 ~(0x80000000L >> (indPrev - 32)); #128 } #129 } #130 #131 // unlink entry from list #132 pPrev->pEntryPrev->pEntryNext = pPrev->pEntryNext; #133 pPrev->pEntryNext->pEntryPrev = pPrev->pEntryPrev; #134 } #135 // set pointer to connect it instead of the free entry #136 pEntry = pPrev; #137 } #104 // if index changed due to coalesing, reconnect to new size #105 if (indPrev != indEntry) // if (2Fh != 8Fh) #106 { #107 // disconnect entry from indPrev #108 // test entry is sole member of bucket (next == prev), #109 if (pPrev->pEntryNext == pPrev->pEntryPrev) #110 { #111 // clear bit in group vector, decrement region count #112 // if region count is now zero, clear bit in header #113 // entry vector #114 if (indPrev < 32) // if (2Fh < 32) #115 { #116 pRegion->bitvGroupHi[indGroup] &= #117 ~(0x80000000L >> indPrev); #118 if (--pRegion->cntRegionSize[indPrev] == 0) #119 pHeader->bitvEntryHi &= ~(0x80000000L >> indPrev); #120 } #121 else #122 { #123 pRegion->bitvGroupLo[indGroup] &= //0xfffeffff #124 ~(0x80000000L >> (indPrev - 32)); #125 if (--pRegion->cntRegionSize[indPrev] == 0) //if (--1 == 0) #126 pHeader->bitvEntryLo &= //0xfffeffff #127 ~(0x80000000L >> (indPrev - 32)); #128 } #129 } #130 #131 // unlink entry from list #132 pPrev->pEntryPrev->pEntryNext = pPrev->pEntryNext; #133 pPrev->pEntryNext->pEntryPrev = pPrev->pEntryPrev; #134 } #135 // set pointer to connect it instead of the free entry #136 pEntry = pPrev; #137 } ㆖方 區塊若 為 free, 合併 之(unlink ㆖ 方區塊 ) 繞過 pPrev(亦即 打斷關 係) 0x00000231 600 0x00000300 0x00000300 300 0x00000301 0x00000231 合併 900 0x00000301 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 21 006c01bc #3fh #3eh 0x00000900 900 007d14cc #3fh #3eh 0x00000900 006c03b4 = 006c01bc + 4 + 3fh * 8 - 4 = 006c01bc + 504 = 006c01bc + 1f8h 006c03b4 #139 // test if previous entry was free with an index change or allocated #140 if (!((sizePrev & 1) == 0 && indPrev == indEntry)) #141 { // !((0x300 & 1)==0 && 0x2f == 0x3f) #142 // connect pEntry entry to indEntry #143 // add entry to the start of the bucket list #144 pHead = (PENTRY)((char *)&pGroup->listHead[indEntry] - sizeof(int)); #145 pEntry->pEntryNext = pHead->pEntryNext; #146 pEntry->pEntryPrev = pHead; #147 pHead->pEntryNext = pEntry; #148 pEntry->pEntryNext->pEntryPrev = pEntry; #149 #150 // test entry is sole member of bucket (next == prev), #151 if (pEntry->pEntryNext == pEntry->pEntryPrev) #152 { #153 // if region count was zero, set bit in region vector #154 // set bit in header entry vector, increment region count #155 if (indEntry < 32) // if (23h < 32) #156 { #157 if (pRegion->cntRegionSize[indEntry]++ == 0) #158 pHeader->bitvEntryHi |= 0x80000000L >> indEntry; #159 pRegion->bitvGroupHi[indGroup] |= 0x80000000L >> indEntry; #160 } #161 else #162 { #163 if (pRegion->cntRegionSize[indEntry]++ == 0) // [3Fh]. if (1++ == 0) #164 pHeader->bitvEntryLo |= 0x80000000L >> (indEntry - 32); #165 pRegion->bitvGroupLo[indGroup] |= 0x80000000L >> #166 (indEntry - 32); #167 } #168 } #169 } #170 #171 // adjust the entry size front and back #172 pEntry->sizeFront = sizeEntry; //900h #173 ((PENTRYEND)((char *)pEntry + sizeEntry - #174 sizeof(ENTRYEND)))->sizeBack = sizeEntry; //900h #139 // test if previous entry was free with an index change or allocated #140 if (!((sizePrev & 1) == 0 && indPrev == indEntry)) #141 { // !((0x300 & 1)==0 && 0x2f == 0x3f) #142 // connect pEntry entry to indEntry #143 // add entry to the start of the bucket list #144 pHead = (PENTRY)((char *)&pGroup->listHead[indEntry] - sizeof(int)); #145 pEntry->pEntryNext = pHead->pEntryNext; #146 pEntry->pEntryPrev = pHead; #147 pHead->pEntryNext = pEntry; #148 pEntry->pEntryNext->pEntryPrev = pEntry; #149 #150 // test entry is sole member of bucket (next == prev), #151 if (pEntry->pEntryNext == pEntry->pEntryPrev) #152 { #153 // if region count was zero, set bit in region vector #154 // set bit in header entry vector, increment region count #155 if (indEntry < 32) // if (23h < 32) #156 { #157 if (pRegion->cntRegionSize[indEntry]++ == 0) #158 pHeader->bitvEntryHi |= 0x80000000L >> indEntry; #159 pRegion->bitvGroupHi[indGroup] |= 0x80000000L >> indEntry; #160 } #161 else #162 { #163 if (pRegion->cntRegionSize[indEntry]++ == 0) // [3Fh]. if (1++ == 0) #164 pHeader->bitvEntryLo |= 0x80000000L >> (indEntry - 32); #165 pRegion->bitvGroupLo[indGroup] |= 0x80000000L >> #166 (indEntry - 32); #167 } #168 } #169 } #170 #171 // adjust the entry size front and back #172 pEntry->sizeFront = sizeEntry; //900h #173 ((PENTRYEND)((char *)pEntry + sizeEntry - #174 sizeof(ENTRYEND)))->sizeBack = sizeEntry; //900h 0x006c03b4 0x006c03b4 0x006c03b4 0x007d14cc 0x007d14cc 也就是將 #0x3f bit 設為 1 : Lo: 0000 0000 0000 0000 0000 0000 0000 0001 由此過程體會出,配置時才從 entry 所指 block 切㆒塊 適當大小來用。但釋放時㆒定串接在特定(取決於 block 大小) 的 HeadList ㆗。 0x00000000 |= 0x80000000 >>(0x3f - 32) Î 0x00000000 |= 0x80000000 >>(31) Î 0x00000000 |= 0x00000001 Î 0x00000001 0x00000231 合併 0x00000301 900 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 22 #176 // one less allocation in group - test if empty #177 if (--pGroup->cntEntries == 0) // if ((-- 0x34) == 0) #178 { #179 // if a group has been deferred, free that group #180 if (__sbh_pHeaderDefer) #181 { #182 // if now zero, decommit the group data heap #183 pHeapDecommit = (void *)((char *)__sbh_pHeaderDefer->pHeapData + #184 __sbh_indGroupDefer * BYTES_PER_GROUP); #185 VirtualFree(pHeapDecommit, BYTES_PER_GROUP, MEM_DECOMMIT); #186 #187 // set bit in commit vector #188 __sbh_pHeaderDefer->bitvCommit |= #189 0x80000000 >> __sbh_indGroupDefer; #190 #191 // clear entry vector for the group and header vector bit #192 // if needed #193 __sbh_pHeaderDefer->pRegion->bitvGroupLo[__sbh_indGroupDefer] = 0; #194 if (--__sbh_pHeaderDefer->pRegion->cntRegionSize[63] == 0) #195 __sbh_pHeaderDefer->bitvEntryLo &= ~0x00000001L; #196 #197 // if commit vector is the initial value, #198 // remove the region if it is not the last #199 if (__sbh_pHeaderDefer->bitvCommit == BITV_COMMIT_INIT) #200 { #201 // release the address space for heap data #202 VirtualFree(__sbh_pHeaderDefer->pHeapData, 0, MEM_RELEASE); #203 #204 // free the region memory area #205 HeapFree(_crtheap, 0, __sbh_pHeaderDefer->pRegion); #206 #176 // one less allocation in group - test if empty #177 if (--pGroup->cntEntries == 0) // if ((-- 0x34) == 0) #178 { #179 // if a group has been deferred, free that group #180 if (__sbh_pHeaderDefer) #181 { #182 // if now zero, decommit the group data heap #183 pHeapDecommit = (void *)((char *)__sbh_pHeaderDefer->pHeapData + #184 __sbh_indGroupDefer * BYTES_PER_GROUP); #185 VirtualFree(pHeapDecommit, BYTES_PER_GROUP, MEM_DECOMMIT); #186 #187 // set bit in commit vector #188 __sbh_pHeaderDefer->bitvCommit |= #189 0x80000000 >> __sbh_indGroupDefer; #190 #191 // clear entry vector for the group and header vector bit #192 // if needed #193 __sbh_pHeaderDefer->pRegion->bitvGroupLo[__sbh_indGroupDefer] = 0; #194 if (--__sbh_pHeaderDefer->pRegion->cntRegionSize[63] == 0) #195 __sbh_pHeaderDefer->bitvEntryLo &= ~0x00000001L; #196 #197 // if commit vector is the initial value, #198 // remove the region if it is not the last #199 if (__sbh_pHeaderDefer->bitvCommit == BITV_COMMIT_INIT) #200 { #201 // release the address space for heap data #202 VirtualFree(__sbh_pHeaderDefer->pHeapData, 0, MEM_RELEASE); #203 #204 // free the region memory area #205 HeapFree(_crtheap, 0, __sbh_pHeaderDefer->pRegion); #206 ㆖述 4 處被喚起的條件是: #177整個 group 的區塊都收回了。 #180有所謂的 Defer-Header(s) 1 2 3 #207 // remove entry from header list by copying over #208 memmove((void *)__sbh_pHeaderDefer, #209 (void *)(__sbh_pHeaderDefer + 1), #210 (int)(__sbh_pHeaderList + __sbh_cntHeaderList) - #211 (int)(__sbh_pHeaderDefer + 1)); #212 __sbh_cntHeaderList--; #213 #214 // if pHeader was after the one just removed, adjust it #215 if (pHeader > __sbh_pHeaderDefer) #216 pHeader--; #217 #218 // initialize scan pointer to start of list #219 __sbh_pHeaderScan = __sbh_pHeaderList; #220 } #221 } #222 #223 // defer the group just freed #224 __sbh_pHeaderDefer = pHeader; #225 __sbh_indGroupDefer = indGroup; #226 } #227 } #207 // remove entry from header list by copying over #208 memmove((void *)__sbh_pHeaderDefer, #209 (void *)(__sbh_pHeaderDefer + 1), #210 (int)(__sbh_pHeaderList + __sbh_cntHeaderList) - #211 (int)(__sbh_pHeaderDefer + 1)); #212 __sbh_cntHeaderList--; #213 #214 // if pHeader was after the one just removed, adjust it #215 if (pHeader > __sbh_pHeaderDefer) #216 pHeader--; #217 #218 // initialize scan pointer to start of list #219 __sbh_pHeaderScan = __sbh_pHeaderList; #220 } #221 } #222 #223 // defer the group just freed #224 __sbh_pHeaderDefer = pHeader; #225 __sbh_indGroupDefer = indGroup; #226 } #227 } 4 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 23 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 1M 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=1 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 0x006c03c0 0x007dF000 0x007d8000 8bytes 保留 0xffffffff 9d0 9d0 0xffffffff 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 00 00 00 00 00 00 00 00 1... ... ... ... ... ... 1 2... ... 64 chars 64 bits,共 32 組 這表示 #0, #1 group 堪用. 007d800c 007d9000 9d0 0-63 個 ListHeads 表現 64 條 freelists 02000014 00000001 00000000 00000001 02000014 00000001 02 #6h,#1bh,#1dh,#3fh 置 1 #31 完成完成完成完成 malloc(3c0h) ÎÎÎÎ __sbh_alloc_block(3e4h) 後的結 果後的結 果後的結 果後的結 果 add 8 bytes entry overhead and round up to next para size 3e4h => 3f0h (#62) => 00000000 00000003 #0, #1 group 都可供應, 選擇法則是: 1. indGroupUse 登記者優先 2. 由 0~31 依序檢查 (結果,本例使用 #1) 9d0 =dc0-3f0 0x000003f1 0x000003f1 3f0 0x00000231 0x00000231 230 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 24 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... 32 個單元 每單元 8 個 pages 1M 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse=1 __sbh_sizeHeaderList=16 __sbh_cntHeaderList = 1 … ... __sbh_pHeaderList (0x007c000c) 0x006c0078 0x007d0000 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0... 0x006c03c0 0x007dF000 0x007d8000 8bytes 保留 0xffffffff ff0 0xffffffff 0 0 0 0 0 0 0 ... ... 0 0 0 0 0 0 0 0 0 0 0 0 0 10 0 0 0 0 0 1 0 0 00 00 00 00 00 00 00 00 1... ... ... ... ... ... 1 2... ... 64 chars 64 bits,共 32 組 這表示 #0, #1 group 堪用. 007d800c 007d9000 ff0 0-63 個 ListHeads 表現 64 條 freelists 02000014 00000001 00000000 00000001 02000014 00000001 0 #6h,#1bh,#1dh,#3fh 置 1 #32 free()先前 從先前 從先前 從先前 從 #1 Group 配置的 兩塊區塊配置的 兩塊區塊配置的 兩塊區塊配置的 兩塊區塊 後的結 果後的結 果後的結 果後的結 果 ff0 雖然 #1 group 已經全回收 (cntEntries == 0), 但沒有還給 system. 因為它被登記為 Defer Header。 __sbh_pHeaderDefer == 007c000c __sbh_indGroupDefer == 1 Hi Lo jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 25 關於關於關於關於 Defer, in __sbh_alloc_block() PHEADER __sbh_pHeaderDefer; int __sbh_indGroupDefer; int __cdecl __sbh_heap_init (void) { __sbh_pHeaderDefer = NULL; … } void * __cdecl __sbh_alloc_block (int intSize) { ... // one more allocation in group - test if group was empty if (pGroup->cntEntries++ == 0) { // if allocating into deferred group, cancel deferral if (pHeader == __sbh_pHeaderDefer && indGroupUse == __sbh_indGroupDefer) __sbh_pHeaderDefer = NULL; } pRegion->indGroupUse = indGroupUse; return (void *)((char *)pEntry + sizeof(int)); } PHEADER __sbh_pHeaderDefer; int __sbh_indGroupDefer; int __cdecl __sbh_heap_init (void) { __sbh_pHeaderDefer = NULL; … } void * __cdecl __sbh_alloc_block (int intSize) { ... // one more allocation in group - test if group was empty if (pGroup->cntEntries++ == 0) { // if allocating into deferred group, cancel deferral if (pHeader == __sbh_pHeaderDefer && indGroupUse == __sbh_indGroupDefer) __sbh_pHeaderDefer = NULL; } pRegion->indGroupUse = indGroupUse; return (void *)((char *)pEntry + sizeof(int)); } sbheap.c 0 Defer 變 數宣告 Defer 變 數初值 Defer 變 數的設 值 and/or 使用 所謂 Defer •__sbh_pHeaderDefer 是 個指標 ,指向 ㆒個全回 收 group 所屬 的 Header。這 個 group 原 本應被 釋放, 但暫時 保留( 延緩釋 放)。 當再 有第 ㆓個全回 收 group 出現 sbh 才釋 放這個 Defer group,並將 新出 現的全回收 group 設為 Defer。 如果尚 未出現 第㆓個全回收 group 而 再次 有所配 置(來 自 Defer group),Defer 指標 會取消 (NULL)。. •__sbh_indGroupDefer 是個 索引, 指出 Region ㆗ 的哪個 (#0~#31) group 是 Defer. 區塊 配置好 後,最 終準備 將 cntEntries 累加 1。 但先檢 查 原本 是否 0 -- 如 果是, 再看此 次所在 的 Header 是否 'Defer' 且此 次所用 的 Group 是否 'Defer'?若 都吻合 就取消 其 'Defer' 身份( 亦即令 __sbh_pHeaderDefer = NULL) 此意 味 'Defer' 必 定是個全回收 Group。全新 Group 不會 是 'Defer',因為 Defer 指標不 可能指 向全新 Group。 Loki 的釋 放也差 不多, 都永遠 保持㆒ 個 全回 收 Chunk (相當於 這裡的全回收 Group) jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 26 關於關於關於關於 Defer, in __sbh_free_block() #176 // one less allocation in group - test if empty #177 if (--pGroup->cntEntries == 0) #178 { #179 // if a group has been deferred, free that group #180 if (__sbh_pHeaderDefer) #181 { #182 // if now zero, decommit the group data heap #183 pHeapDecommit = (void *)((char *)__sbh_pHeaderDefer->pHeapData + #184 __sbh_indGroupDefer * BYTES_PER_GROUP); #185 VirtualFree(pHeapDecommit, BYTES_PER_GROUP, MEM_DECOMMIT); #186 #187 // set bit in commit vector #188 __sbh_pHeaderDefer->bitvCommit |= #189 0x80000000 >> __sbh_indGroupDefer; #190 #191 // clear entry vector for the group and header vector bit #192 // if needed #193 __sbh_pHeaderDefer->pRegion->bitvGroupLo[__sbh_indGroupDefer] = 0; #194 if (--__sbh_pHeaderDefer->pRegion->cntRegionSize[63] == 0) #195 __sbh_pHeaderDefer->bitvEntryLo &= ~0x00000001L; #196 #197 // if commit vector is the initial value, #198 // remove the region if it is not the last #199 if (__sbh_pHeaderDefer->bitvCommit == BITV_COMMIT_INIT) #200 { #201 // release the address space for heap data #202 VirtualFree(__sbh_pHeaderDefer->pHeapData, 0, MEM_RELEASE); #203 #204 // free the region memory area #205 HeapFree(_crtheap, 0, __sbh_pHeaderDefer->pRegion); #206 #176 // one less allocation in group - test if empty #177 if (--pGroup->cntEntries == 0) #178 { #179 // if a group has been deferred, free that group #180 if (__sbh_pHeaderDefer) #181 { #182 // if now zero, decommit the group data heap #183 pHeapDecommit = (void *)((char *)__sbh_pHeaderDefer->pHeapData + #184 __sbh_indGroupDefer * BYTES_PER_GROUP); #185 VirtualFree(pHeapDecommit, BYTES_PER_GROUP, MEM_DECOMMIT); #186 #187 // set bit in commit vector #188 __sbh_pHeaderDefer->bitvCommit |= #189 0x80000000 >> __sbh_indGroupDefer; #190 #191 // clear entry vector for the group and header vector bit #192 // if needed #193 __sbh_pHeaderDefer->pRegion->bitvGroupLo[__sbh_indGroupDefer] = 0; #194 if (--__sbh_pHeaderDefer->pRegion->cntRegionSize[63] == 0) #195 __sbh_pHeaderDefer->bitvEntryLo &= ~0x00000001L; #196 #197 // if commit vector is the initial value, #198 // remove the region if it is not the last #199 if (__sbh_pHeaderDefer->bitvCommit == BITV_COMMIT_INIT) #200 { #201 // release the address space for heap data #202 VirtualFree(__sbh_pHeaderDefer->pHeapData, 0, MEM_RELEASE); #203 #204 // free the region memory area #205 HeapFree(_crtheap, 0, __sbh_pHeaderDefer->pRegion); #206 1 2 3 if (目前 這個是 全回收) //那 ㆒定合 併後掛 在 #63 list. if (先前 存在 'Defer' Header) pHeapData __sbh_pHeaderDefer #0 #17 1M 虛 擬 位 址 空 間 __sbh_indGroupDefer * BYTES_PER_GROUP pHeapDecommit 釋放 'Defered' 記憶 體 (1group) 修改 Defer Header 的記錄 1 Lo[17] (in Region): 1 n if (減 1 之後 == 0) 0 Lo (in Header): 表示 'Defer' Group 不可用 (已釋放) #define BITV_COMMIT_INIT (((1 << GROUPS_PER_REGION) - 1) << \ (32 - GROUPS_PER_REGION)) 也就是 (((1 << 32) - 1) << (32 - 32)),也就是 0xFFFFFFFF if (此 Header 的所有 Groups 都 已不可 用) 把整 個 1MB 虛擬位 址空間 釋放 把整 個 Region 所佔 的 Heap 空間 釋放 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 Defer group ㆒定只 #63 list 串接有 區塊. 減 1表示 groups ㆗ #63 list 帶有區 塊者少 1 個. 如果減1之 後為 0,表 示 groups ㆗ #63 list 帶有 區塊者 個數 為 0,那麼 Header ㆗的 合成 (OR) bit 應 修改為 0 Defer 變 數的設 值 and/or 使用__sbh_free_block() jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 27 關於關於關於關於 Defer, in __sbh_free_block() #207 // remove entry from header list by copying over #208 memmove((void *)__sbh_pHeaderDefer, #209 (void *)(__sbh_pHeaderDefer + 1), #210 (int)(__sbh_pHeaderList + __sbh_cntHeaderList) - #211 (int)(__sbh_pHeaderDefer + 1)); #212 __sbh_cntHeaderList--; #213 #214 // if pHeader was after the one just removed, adjust it #215 if (pHeader > __sbh_pHeaderDefer) #216 pHeader--; #217 #218 // initialize scan pointer to start of list #219 __sbh_pHeaderScan = __sbh_pHeaderList; #220 } #221 } #222 #223 // defer the group just freed #224 __sbh_pHeaderDefer = pHeader; #225 __sbh_indGroupDefer = indGroup; #226 } #227 } #207 // remove entry from header list by copying over #208 memmove((void *)__sbh_pHeaderDefer, #209 (void *)(__sbh_pHeaderDefer + 1), #210 (int)(__sbh_pHeaderList + __sbh_cntHeaderList) - #211 (int)(__sbh_pHeaderDefer + 1)); #212 __sbh_cntHeaderList--; #213 #214 // if pHeader was after the one just removed, adjust it #215 if (pHeader > __sbh_pHeaderDefer) #216 pHeader--; #217 #218 // initialize scan pointer to start of list #219 __sbh_pHeaderScan = __sbh_pHeaderList; #220 } #221 } #222 #223 // defer the group just freed #224 __sbh_pHeaderDefer = pHeader; #225 __sbh_indGroupDefer = indGroup; #226 } #227 } 4 將全回 收 group 視為 'Defer' group. void *memmove( void *dest, const void *src, size_t count ); The memmove function copies count bytes of characters from src to dest. If some regions of the source area and the destination overlap, memmove ensures that the original source bytes in the overlapping region are copied before being overwritten. void *memmove( void *dest, const void *src, size_t count ); The memmove function copies count bytes of characters from src to dest. If some regions of the source area and the destination overlap, memmove ensures that the original source bytes in the overlapping region are copied before being overwritten. __sbh_pHeaderDefer (dest) __sbh_pHeaderDefer + 1 (src) __sbh_pHeaderList … ... (int)(__sbh_pHeaderList + __sbh_cntHeaderList) - (int)(__sbh_pHeaderDefer + 1)); … ... … ...長度 -1 memmove如果 pHeader 落在被 搬移範 圍內, 由於整 個範 圍是 向低位 址搬移 ㆒個 Header,所 以 pHeader 也必 須移動 ㆒個 Header 位置 (count) jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 28 關於關於關於關於 Defer, 測試測試測試測試 先前 的 #27, #31 測 試(進 入 #1 group), 釋放後 cntEntries == 0, 於是 #1 group 被標示 為 'Defer'。(如右 圖) Q: 全部 歸還後 ㆒定( 依舊) 是 #63 串起 8 大塊 4Kb 嗎? A: … ( 至少可 肯定的 是只有 #63 串 有區塊 ) 接㆘ 來: void* p_1[24]; void* p_2[24]; for(i=0; i<24; ++i) p_1[i] = malloc(0x3D4); // #1 group 只剩 8 個 1008 bytes, 都掛在 #62. // ff0-(3d4+24+8)*3 = ff0-c00 = 3f0 = 1008. for(i=0; i<24; ++i) p_2[i] = malloc(0x3D4); // #2 group 只剩 8 個 1008 bytes, 都掛在 #62. for(i=0; i<24; ++i) free(p_1[i]); // #1 group 全 回收, 被標示 為 'Defer' for(i=0; i<24; ++i) free(p_2[i]); // #2 group 全 回收, 釋放 'Defer' 並將 #2 group 標為 'Defer' page0 page2 page3 page4 page5 page6 page7 page8 保留 0xffffffff ff0 ff0 0xffffffff 保留 0xffffffff ff0 保留 0xffffffff ff0 保留 0xffffffff ff0 保留 0xffffffff ff0 保留 0xffffffff ff0 保留 0xffffffff ff0 保留 0xffffffff ff0 ff0 0xffffffff ff0 0xffffffff ff0 0xffffffff ff0 0xffffffff ff0 0xffffffff ff0 0xffffffff ff0 0xffffffff Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... __sbh_pHeaderDefer __sbh_indGroupDefer == 1 … ... jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 29 關於關於關於關於 Scan, PHEADER __sbh_pHeaderScan; int __cdecl __sbh_heap_init (void) { __sbh_pHeaderScan = __sbh_pHeaderList; … } PHEADER __sbh_pHeaderScan; int __cdecl __sbh_heap_init (void) { __sbh_pHeaderScan = __sbh_pHeaderList; … } sbheap.c Scan 變數宣 告 #001 void __cdecl __sbh_free_block (PHEADER pHeader, void * pvAlloc) #002 { ... #197 // if commit vector is the initial value, #198 // remove the region if it is not the last #199 if (__sbh_pHeaderDefer->bitvCommit == BITV_COMMIT_INIT) #200 { ... #218 // initialize scan pointer to start of list #219 __sbh_pHeaderScan = __sbh_pHeaderList; #220 } #221 } ... #226 } #227 } #001 void __cdecl __sbh_free_block (PHEADER pHeader, void * pvAlloc) #002 { ... #197 // if commit vector is the initial value, #198 // remove the region if it is not the last #199 if (__sbh_pHeaderDefer->bitvCommit == BITV_COMMIT_INIT) #200 { ... #218 // initialize scan pointer to start of list #219 __sbh_pHeaderScan = __sbh_pHeaderList; #220 } #221 } ... #226 } #227 } if (此 Header 的所有 Groups 都已不可用) 把整個 1MB 虛擬位址空間釋放 VirtualFree() 把整個 Region 所佔的 Heap 空間釋放 HeapFree() 搬移 Headers memmove() Scan 變數的 設值 jjhou@jjhou.com http://www.jjhou.com http://jjhou.csdn.net 30 關於關於關於關於 Scan, #001 void * __cdecl __sbh_alloc_block (int intSize) #002 { ... #022 // determine index and mask from entry size ... #045 // scan header list from rover to end for region with a free #046 // entry with an adequate size #047 pHeader = __sbh_pHeaderScan; #048 while (pHeader < pHeaderLast) #049 { #050 if ((bitvEntryHi & pHeader->bitvEntryHi) | #051 (bitvEntryLo & pHeader->bitvEntryLo)) #052 break; #053 pHeader++; #054 } #055 #056 // if no entry, scan from list start up to the rover #057 if (pHeader == pHeaderLast) #058 { #059 pHeader = __sbh_pHeaderList; #060 while (pHeader < __sbh_pHeaderScan) #061 { #062 if ((bitvEntryHi & pHeader->bitvEntryHi) | #063 (bitvEntryLo & pHeader->bitvEntryLo)) #064 break; #065 pHeader++; #066 } #067 #068 // no free entry exists, scan list from rover to end #069 // for available groups to commit #070 if (pHeader == __sbh_pHeaderScan) #071 { #072 while (pHeader < pHeaderLast) #073 { #074 if (pHeader->bitvCommit) #075 break; #076 pHeader++; #001 void * __cdecl __sbh_alloc_block (int intSize) #002 { ... #022 // determine index and mask from entry size ... #045 // scan header list from rover to end for region with a free #046 // entry with an adequate size #047 pHeader = __sbh_pHeaderScan; #048 while (pHeader < pHeaderLast) #049 { #050 if ((bitvEntryHi & pHeader->bitvEntryHi) | #051 (bitvEntryLo & pHeader->bitvEntryLo)) #052 break; #053 pHeader++; #054 } #055 #056 // if no entry, scan from list start up to the rover #057 if (pHeader == pHeaderLast) #058 { #059 pHeader = __sbh_pHeaderList; #060 while (pHeader < __sbh_pHeaderScan) #061 { #062 if ((bitvEntryHi & pHeader->bitvEntryHi) | #063 (bitvEntryLo & pHeader->bitvEntryLo)) #064 break; #065 pHeader++; #066 } #067 #068 // no free entry exists, scan list from rover to end #069 // for available groups to commit #070 if (pHeader == __sbh_pHeaderScan) #071 { #072 while (pHeader < pHeaderLast) #073 { #074 if (pHeader->bitvCommit) #075 break; #076 pHeader++; #0 } Scan 變數的 設值與 使用 第 1 段 需求端64bits 比對 供應端64bits 第 2 段 需求端64bits 比對 供應端64bits ... scan last12list 以㆖沒找到 以㆖沒找到 第 1 段 有無 未配置 group? 所謂 Scan •__sbh_pHeaderScan 是個 指標, 指向最 近曾 配置過 區塊的 group 的所 屬 Header。 •每當 要新配 置區塊 ,就從 Scan 掃描 到 Last,再 從 First 掃描 到 Scan,看看 哪㆒ 個 Header (的Region) 有可 用 group。 •如果 以㆖都 沒找到 ,再次 從 Scan 掃描到 Last,以 及從 First 掃描 到 Scan,看 看哪 個 Header (的Region) 有尚 未配置 的 group 配額 。若有 就 new a group •如果 還是找 不到, 就是所 有 Headers (的 Region) 的所有 32 groups 都配 置了且 都 沒有 可用區 塊了。 那就 new a region. Loki 的搜 尋也差 不多, 都是利 用標兵 jjhou.928@gmail.com 1 mainCRTStartup in VC6+ 侯捷侯捷侯捷侯捷 2009/11 main() 啟動前後的完整過程啟動前後的完整過程啟動前後的完整過程啟動前後的完整過程 jjhou.928@gmail.com 3 ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() // do C data initialize _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() // initialize lowio __sbh_heap_init() _heap_init(...) mainCRTStartup() KERNEL32! bff8b6e6() KERNEL32! bff8b598() KERNEL32! bff89f5b() ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() // do C data initialize _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() // initialize lowio __sbh_heap_init() _heap_init(...) mainCRTStartup() KERNEL32! bff8b6e6() KERNEL32! bff8b598() KERNEL32! bff89f5b() crt0.c 見後 mainCRTStartup() void mainCRTStartup(void) { int mainret; // Get the full Win32 version _osver = GetVersion(); _winminor = (_osver >> 8) & 0x00FF ; _winmajor = _osver & 0x00FF ; _winver = (_winmajor << 8) + _winminor; _osver = (_osver >> 16) & 0x00FFFF ; if ( !_heap_init(0) ) /* initialize heap */ fast_error_exit(_RT_HEAPINIT); /* write message and die */ void mainCRTStartup(void) { int mainret; // Get the full Win32 version _osver = GetVersion(); _winminor = (_osver >> 8) & 0x00FF ; _winmajor = _osver & 0x00FF ; _winver = (_winmajor << 8) + _winminor; _osver = (_osver >> 16) & 0x00FFFF ; if ( !_heap_init(0) ) /* initialize heap */ fast_error_exit(_RT_HEAPINIT); /* write message and die */ 左邊 的 call stack 完全 與右邊 的 source 對應( 當然! ) 01 02 03 04 05 06 07 08 09 10 11 12 13 in Windows 98: _osver = 0x0000c000 _winminor = 0x0000000a _winmajor = 0x00000004 _winver = 0x0000040a _osver = 0x0000c000 這是 整理後 的結果 。實際 有很多 #ifdef,有 的進入 WinMain() 有的 進入 main(),見 後頁 接㆘ 頁 0 1 in Windows XP: _osver = 2600 _winminor = 1 _winmajor = 5 _winver = 1281 _osver = 2600 Q: 為什 麼在 crt0.c 和 crtexe.c 都有 mainCRTStartup()? A: 前者 是actual startup routine for apps, 後者是Initialization for client EXE using CRT DLL (Win32, Dosx32) 本例 喚起的 是 crt0.c 的那 個,源 碼如㆘ 。 jjhou.928@gmail.com 4 cmdln, argc, argv in mainCRTStartup() /* * Guard the remainder of the initialization code and the call * to user's main, or WinMain, function in a __try/__except * statement. */ __try { _ioinit(); /* initialize lowio */ /* get cmd line info */ _acmdln = (char *)GetCommandLineA(); /* get environ info */ _aenvptr = (char *)__crtGetEnvironmentStringsA(); _setargv(); _setenvp(); _cinit(); /* do C data initialize */ __initenv = _environ; mainret = main(__argc, __argv, _environ); exit(mainret); } __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) ) { // Should never reach here _exit( GetExceptionCode() ); } /* end of try - except */ } /* * Guard the remainder of the initialization code and the call * to user's main, or WinMain, function in a __try/__except * statement. */ __try { _ioinit(); /* initialize lowio */ /* get cmd line info */ _acmdln = (char *)GetCommandLineA(); /* get environ info */ _aenvptr = (char *)__crtGetEnvironmentStringsA(); _setargv(); _setenvp(); _cinit(); /* do C data initialize */ __initenv = _environ; mainret = main(__argc, __argv, _environ); exit(mainret); } __except ( _XcptFilter(GetExceptionCode(), GetExceptionInformation()) ) { // Should never reach here _exit( GetExceptionCode() ); } /* end of try - except */ } 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 mainCRTStartup() in crt0.c 例:e:\handout\test\objlist\debug\objlife.exe 續㆖ 頁 0x00000001 2 3 4 5 6 7 8 9 0x007d0be0 見㆘ 頁 _setargv() 之內 決定 __argc 和 __argv At startup, we obtain the 'native' flavor of environment strings from the OS. So a "main" program has _environ and a "wmain" has _wenviron loaded at startup. Only when the user gets or puts the 'other' flavor do we convert it. jjhou.928@gmail.com 5 #ifdef _WINMAIN_ StartupInfo.dwFlags = 0; GetStartupInfo( &StartupInfo ); #ifdef WPRFLAG lpszCommandLine = _wwincmdln(); mainret = wWinMain( #else /* WPRFLAG */ lpszCommandLine = _wincmdln(); mainret = WinMain( #endif /* WPRFLAG */ GetModuleHandleA(NULL), NULL, lpszCommandLine, StartupInfo.dwFlags & STARTF_USESHOWWINDOW ? StartupInfo.wShowWindow : SW_SHOWDEFAULT ); #else /* _WINMAIN_ */ #ifdef WPRFLAG __winitenv = _wenviron; mainret = wmain(__argc, __wargv, _wenviron); #else /* WPRFLAG */ __initenv = _environ; mainret = main(__argc, __argv, _environ); #endif /* WPRFLAG */ #endif /* _WINMAIN_ */ #ifdef _WINMAIN_ StartupInfo.dwFlags = 0; GetStartupInfo( &StartupInfo ); #ifdef WPRFLAG lpszCommandLine = _wwincmdln(); mainret = wWinMain( #else /* WPRFLAG */ lpszCommandLine = _wincmdln(); mainret = WinMain( #endif /* WPRFLAG */ GetModuleHandleA(NULL), NULL, lpszCommandLine, StartupInfo.dwFlags & STARTF_USESHOWWINDOW ? StartupInfo.wShowWindow : SW_SHOWDEFAULT ); #else /* _WINMAIN_ */ #ifdef WPRFLAG __winitenv = _wenviron; mainret = wmain(__argc, __wargv, _wenviron); #else /* WPRFLAG */ __initenv = _environ; mainret = main(__argc, __argv, _environ); #endif /* WPRFLAG */ #endif /* _WINMAIN_ */ mainCRTStartup() 局部, in crt0.c 很多#ifdef, 有的 進入 WinMain(), 有的 進入 main() mainCRTStartup() ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() 若非 呼叫 WinMain() 就是 呼叫 main(...), 又各 有 w 版本 和 non-w 版本 。 jjhou.928@gmail.com 6 _heap_init(...) ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() /*** *_heap_init() - Initialize the heap *Purpose: * Setup the initial C library heap. * NOTES: * (1) This routine should only be called once! * (2) This routine must be called before any other heap requests. *Entry: * *Exit: * Returns 1 if successful, 0 otherwise. *Exceptions: * If heap cannot be initialized, the program will be terminated * with a fatal runtime error. *******************************************************/ int __cdecl _heap_init (int mtflag) { // Initialize the "big-block" heap first. if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL ) return 0; // Initialize the small-block heap if (__sbh_heap_init() == 0) { HeapDestroy(_crtheap); return 0; } return 1; } /*** *_heap_init() - Initialize the heap *Purpose: * Setup the initial C library heap. * NOTES: * (1) This routine should only be called once! * (2) This routine must be called before any other heap requests. *Entry: * *Exit: * Returns 1 if successful, 0 otherwise. *Exceptions: * If heap cannot be initialized, the program will be terminated * with a fatal runtime error. *******************************************************/ int __cdecl _heap_init (int mtflag) { // Initialize the "big-block" heap first. if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL ) return 0; // Initialize the small-block heap if (__sbh_heap_init() == 0) { HeapDestroy(_crtheap); return 0; } return 1; } 完整 源碼 (in heapinit.c)01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 ㆘頁 4096, initial heap size jjhou.928@gmail.com 7 int __cdecl _heap_init (int mtflag) { // Initialize the "big-block" heap first. if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL ) return 0; // Initialize the small-block heap if (__sbh_heap_init() == 0) { HeapDestroy(_crtheap); return 0; } return 1; } int __cdecl _heap_init (int mtflag) { // Initialize the "big-block" heap first. if ( (_crtheap = HeapCreate( mtflag ? 0 : HEAP_NO_SERIALIZE, BYTES_PER_PAGE, 0 )) == NULL ) return 0; // Initialize the small-block heap if (__sbh_heap_init() == 0) { HeapDestroy(_crtheap); return 0; } return 1; } /*** *int __sbh_heap_init() - set small-block heap threshold * *Purpose: * Allocate space for initial header list and init variables. *Entry: * None. *Exit: * Returns 1 if successful. Returns 0 if initialization failed. *Exceptions: ***************************************************** int __cdecl __sbh_heap_init (void) { if (!(__sbh_pHeaderList = HeapAlloc(_crtheap, 0, 16 * sizeof(HEADER)))) return FALSE; __sbh_pHeaderScan = __sbh_pHeaderList; __sbh_pHeaderDefer = NULL; __sbh_cntHeaderList = 0; __sbh_sizeHeaderList = 16; return TRUE; } /*** *int __sbh_heap_init() - set small-block heap threshold * *Purpose: * Allocate space for initial header list and init variables. *Entry: * None. *Exit: * Returns 1 if successful. Returns 0 if initialization failed. *Exceptions: ***************************************************** int __cdecl __sbh_heap_init (void) { if (!(__sbh_pHeaderList = HeapAlloc(_crtheap, 0, 16 * sizeof(HEADER)))) return FALSE; __sbh_pHeaderScan = __sbh_pHeaderList; __sbh_pHeaderDefer = NULL; __sbh_cntHeaderList = 0; __sbh_sizeHeaderList = 16; return TRUE; } 完整 源碼 (in sbheap.c) __sbh_heap_init() number of bytes to allocate 此函 式從稍早 以 HeapCreate() 建立 的 heap ㆗, 使用 HeapAlloc() 配置 足供 16 個 Headers 的空 間 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 見後 jjhou.928@gmail.com 9 __sbh_alloc_new_region(), __sbh_alloc_group() ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() /*** *PHEADER __sbh_alloc_new_region() *Purpose: * Add a new HEADER structure in the header list. Allocate a new * REGION structure and initialize. Reserve memory for future * group commitments. *Entry: * None. *Exit: * Returns a pointer to newly created HEADER entry, if successful. * Returns NULL, if failure. *Exceptions: *******************************************************/ /*** *PHEADER __sbh_alloc_new_region() *Purpose: * Add a new HEADER structure in the header list. Allocate a new * REGION structure and initialize. Reserve memory for future * group commitments. *Entry: * None. *Exit: * Returns a pointer to newly created HEADER entry, if successful. * Returns NULL, if failure. *Exceptions: *******************************************************/ /*** *int __sbh_alloc_new_group(pHeader) *Purpose: * Initializes a GROUP structure within HEADER pointed by pHeader. * Commits and initializes the memory in the memory reserved by the * REGION. *Entry: * pHeader - pointer to HEADER from which the GROUP is defined. *Exit: * Returns an index to newly created GROUP, if successful. * Returns -1, if failure. *Exceptions: **********************************************************/ /*** *int __sbh_alloc_new_group(pHeader) *Purpose: * Initializes a GROUP structure within HEADER pointed by pHeader. * Commits and initializes the memory in the memory reserved by the * REGION. *Entry: * pHeader - pointer to HEADER from which the GROUP is defined. *Exit: * Returns an index to newly created GROUP, if successful. * Returns -1, if failure. *Exceptions: **********************************************************/ sbheap.c sbheap.c jjhou.928@gmail.com 10 Group0Group0 Group1Group1 Group2Group2 Group3Group3 Group4Group4 Group5Group5 Group6Group6 Group7Group7 Group30Group30 Group31Group31 ... #6 group 4096 (扣掉overhead是4080) 每次添加16個Headers,用 光了再配置16個…每個 Header 表現㆒個 Region. region 內含 32 groups 每個 group 有 8 pages 每單元 (group) 8 個 pages 虛 擬 位 址 空 間 32 個Groups pRegion pHeapData indGroupUse __sbh_sizeHeaderList __sbh_cntHeaderList … ... __sbh_pHeaderList #n 組 64 bits 負責記錄 #n Group 的 64 個 entries 的情況 #n 元素記錄 #n 縱列之 bits 'or'。 bit 值為1 之個數 (最多 32) 被記錄於cntRegionSize[n]. #n 元素若為1,表示 32 個 groups ㆗的至少㆒個的 #n ListHead 掛著可用區塊. 0表示對應之group 堪用 詳情見㆘頁 cntRegionSize[64] 目前 使用的 group 編號. 這 些 資 料 用 來 控 制 實 際 region (1MB) 的 使 用 region VirtualAlloc( ,1M,MEM_RESERVE, ) VirtualAlloc( ,32K, MEM_COMMIT, ) HeapAlloc(,,16*sizeof(HEADER)) HeapAlloc(,,sizeof(REGION)); __sbh_alloc_new_region(), __sbh_alloc_group() jjhou.928@gmail.com 11 page0 page2 page3 page4 page5 page6 page7 page8 保留 0xffffffff 4080 4080 0xffffffff 保留 0xffffffff 4080 保留 0xffffffff 4080 保留 0xffffffff 4080 保留 0xffffffff 4080 保留 0xffffffff 4080 保留 0xffffffff 4080 保留 0xffffffff 4080 4080 0xffffffff 4080 0xffffffff 4080 0xffffffff 4080 0xffffffff 4080 0xffffffff 4080 0xffffffff 4080 0xffffffff __sbh_alloc_new_group() jjhou.928@gmail.com 12 main() _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(blockSize) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() KERNEL32! bff8b6e6() KERNEL32! bff8b598() KERNEL32! bff89f5b() main() _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(blockSize) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() KERNEL32! bff8b6e6() KERNEL32! bff8b598() KERNEL32! bff89f5b() 把 break point 設在 __sbh_alloc_block() 便可觀 察整個 process 從 sbh (small block heap) 取 記憶體 的次數 和數量 124h / 292 => 304 (#18) 230h / 560 => 560 (#34) 64h / 100 => 112 (#6) 62h / 98 => 112 (#6) 102h / 258 => 272 (#16) 36h / 54 => 64 (#3) 30h / 48 => 48 (#2) 77h / 119 => 128 (#7) 43h / 67 => 80 (#4) 3ah /58 => 64 (#3) 30h / 48 => 48 (#2) 39h / 57 => 64 (#3) 38h / 56 => 64 (#3) 50h / 80 => 80 (#4) b4h / 180 => 192 (#11) (注意,此後有㆒次free) 45h / 69 => 80 (#4) 4ch / 76 =>80 (#4) a4h / 164 => 176 (#10)(注意,此前有㆒次free) blocks allocated before main() 進入main() 之前 ,CRT 已經做 了許多 工作, 這些工 作曾經 索求若 干記憶 體(以HeapAlloc() 和 VirtualAlloc() 獲得 ),因 此當 main() 內呼 叫 malloc() 時 ,初始 已有若 干 blocks 掛在 sbh (small blocks heap) 維護 的 free lists ㆖, 此時不 需喚起 HeapAlloc() 和或 VirtualAlloc() 向作業 系統配 置。 _initterm( __xi_a, __xi_z ); 紫色 數字表 示 __sbh_alloc_block(...) 收到的 blockSize,這 個數 字將在 該函式 內被調 整為 16 倍 數值, 然後計 算出落 在第 幾個 entry (free-list) 內。 jjhou.928@gmail.com 20 pointers to initializers and terminators /* * pointers to initialization functions */ #pragma data_seg(".CRT$XIA") PFV __xi_a = 0; /* C initializers */ #pragma data_seg(".CRT$XIZ") PFV __xi_z = 0; #pragma data_seg(".CRT$XCA") PFV __xc_a = 0; /* C++ initializers */ #pragma data_seg(".CRT$XCZ") PFV __xc_z = 0; #pragma data_seg(".CRT$XPA") PFV __xp_a = 0; /* C pre-terminators */ #pragma data_seg(".CRT$XPZ") PFV __xp_z = 0; #pragma data_seg(".CRT$XTA") PFV __xt_a = 0; /* C terminators */ #pragma data_seg(".CRT$XTZ") PFV __xt_z = 0; #pragma data_seg() /* reset */ /* * pointers to initialization functions */ #pragma data_seg(".CRT$XIA") PFV __xi_a = 0; /* C initializers */ #pragma data_seg(".CRT$XIZ") PFV __xi_z = 0; #pragma data_seg(".CRT$XCA") PFV __xc_a = 0; /* C++ initializers */ #pragma data_seg(".CRT$XCZ") PFV __xc_z = 0; #pragma data_seg(".CRT$XPA") PFV __xp_a = 0; /* C pre-terminators */ #pragma data_seg(".CRT$XPZ") PFV __xp_z = 0; #pragma data_seg(".CRT$XTA") PFV __xt_a = 0; /* C terminators */ #pragma data_seg(".CRT$XTZ") PFV __xt_z = 0; #pragma data_seg() /* reset */ ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() ExitProcess(code) _initterm(,,) //do terminators __endstdio(void) _initterm(,,) //do pre-terminators doexit(code, 0, 0) exit(code) main() _initterm(,,) //do C++ initializations __initstdio(void) _initterm(,,) //do initializations _cinit() _setenvp() _setargv() __crtGetEnvironmentStringsA() GetCommandLineA() __sbh_alloc_new_group(...) __sbh_alloc_new_region() __sbh_alloc_block(...) _heap_alloc_base(...) _heap_alloc_dbg(...) _nh_malloc_dbg(...) _malloc_dbg(...) _ioinit() __sbh_heap_init() _heap_init(...) mainCRTStartup() cinitexe.c File fltintrn.h: typedef void (* PFV)(void); 這 些 變 數 在 哪 兒 被 設 值 ? 右 邊 ㆕ 組 變 數 在 左 邊 ㆕ 個 ㆞ 方 被 使 用 jjhou.928@gmail.com 21 __cinit(), do initializations void __cdecl _cinit ( void ) { /* * initialize floating point package, if present */ #if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) /* * MIPS compiler doesn't emit external reference to _fltused. Therefore, * must always force in the floating point initialization. */ _fpmath(); #else /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */ if ( _FPinit != NULL ) (*_FPinit)(); #endif /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */ /* * do initializations */ _initterm( __xi_a, __xi_z ); /* * do C++ initializations */ _initterm( __xc_a, __xc_z ); } void __cdecl _cinit ( void ) { /* * initialize floating point package, if present */ #if defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) /* * MIPS compiler doesn't emit external reference to _fltused. Therefore, * must always force in the floating point initialization. */ _fpmath(); #else /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */ if ( _FPinit != NULL ) (*_FPinit)(); #endif /* defined (_M_MRX000) || defined (_M_ALPHA) || defined (_M_PPC) */ /* * do initializations */ _initterm( __xi_a, __xi_z ); /* * do C++ initializations */ _initterm( __xc_a, __xc_z ); } static void __cdecl _initterm (_PVFV * pfbegin, _PVFV * pfend ) { while ( pfbegin < pfend ) { // if current table entry is non-NULL, call thru it. if ( *pfbegin != NULL ) (**pfbegin)(); ++pfbegin; } } static void __cdecl _initterm (_PVFV * pfbegin, _PVFV * pfend ) { while ( pfbegin < pfend ) { // if current table entry is non-NULL, call thru it. if ( *pfbegin != NULL ) (**pfbegin)(); ++pfbegin; } } __initstdio(void) __initmbctable(void) __CxxSetUnhandledExceptionFilter(void) ... ... __onexitinit(void) ... ... 如何 得知這 些函式 ?設㆗ 斷點於 sbh_alloc_bloc() 後可 從 call stock 看到 若干函 式名稱 File internal.h: typedef void (__cdecl *_PVFV)(void); 見㆘ 頁 見後 __xi_a __xi_z ex: 0x00474390, 0x004746a8
还剩150页未读

继续阅读

下载pdf到电脑,查找使用更方便

pdf的实际排版效果,会与网站的显示效果略有不同!!

需要 10 金币 [ 分享pdf获得金币 ] 0 人已下载

下载pdf

pdf贡献者

craigxu

贡献于2014-08-01

下载需要 10 金币 [金币充值 ]
亲,您也可以通过 分享原创pdf 来获得金币奖励!
下载pdf