Arpan Sen, 技術(shù)負(fù)責(zé)人, Synapti Computer Aided Design Pvt Ltd
簡介: 代碼的性能優(yōu)化是一項(xiàng)非常重要的工作。經(jīng)常可以看到,采用 C 或 C++ 編寫的、功能正確的軟件
在執(zhí)行時(shí)耗費(fèi)大量的內(nèi)存、時(shí)間、或者在最糟的情況下既耗內(nèi)存又費(fèi)時(shí)間。作為一名開發(fā)人員,可以使用
C/C++ 提供的功能強(qiáng)大的工具來改進(jìn)處理時(shí)間,并且防止內(nèi)存破壞,這些工具其中之一是控制如何在代碼
中分配或者釋放內(nèi)存。通過介紹如何針對(duì)特定的情況創(chuàng)建自己的內(nèi)存管理器,本教程對(duì)內(nèi)存管理的相關(guān)
概念進(jìn)行了揭秘。
開始之前
了解本教程中包含的內(nèi)容以及如何最好地利用本教程。
關(guān)于本教程
本教程采用了一種基本方法為任何應(yīng)用程序構(gòu)建內(nèi)存管理器。本教程解釋了為什么需要內(nèi)存管理器,并提供
了為應(yīng)用程序編寫自定義的內(nèi)存管理器(以滿足特定的需求)的方法。
目標(biāo)
在本教程中,您將了解在設(shè)計(jì)內(nèi)存管理器之前需要考慮的一些注意事項(xiàng)、可用于創(chuàng)建這種內(nèi)存管理器的一些
特定技術(shù),并在文章的最后了解創(chuàng)建內(nèi)存管理器的方法。您還將了解各種類型內(nèi)存管理器設(shè)計(jì)的優(yōu)點(diǎn)和不足
之處。
先決條件
本教程面向那些入門級(jí)到中級(jí)水平的 Linux® 或 UNIX® 程序員。您應(yīng)該對(duì)使用 UNIX 命令行 Shell,
以及 C/C++ 語言的應(yīng)用知識(shí)有基本的了解。此外,還需要了解 malloc
、calloc
、free
、memcpy
和
memset
等系統(tǒng)調(diào)用(即處理內(nèi)存分配、釋放和內(nèi)容修改的例程)的內(nèi)部工作方式。
系統(tǒng)要求
要運(yùn)行本教程中的示例,您需要具備安裝了 g++ 編譯器工具鏈的 Linux 或者 UNIX 系統(tǒng)。還需要具有
足夠大的 RAM(大約 256 MB)。
為什么要?jiǎng)?chuàng)建自定義的內(nèi)存管理器呢?
要理解內(nèi)存分配控制如何幫助提高代碼的運(yùn)行速度,首先需要回憶一下 C/C++ 中內(nèi)存管理的基礎(chǔ)知識(shí)。
C 中的標(biāo)準(zhǔn)庫函數(shù)malloc
、free
、calloc
和 realloc
,以及 C++ 中的 new
、new [ ]
、delete
和 delete
[ ]
操作符,是這兩種語言中內(nèi)存管理的關(guān)鍵之處。在了解了這一點(diǎn)之后,有幾個(gè)內(nèi)容值得注意。
如 malloc
和 new
函數(shù)是通用的內(nèi)存分配器。您的代碼可能是單線程的,但它所鏈接的 malloc
函數(shù)同樣
可以處理多線程的范例。正是由于這個(gè)額外功能,使得這些例程的性能有所降低。
在執(zhí)行時(shí),malloc
和 new
將向操作系統(tǒng)內(nèi)核請(qǐng)求內(nèi)存,而 free
和 delete
則請(qǐng)求釋放內(nèi)存。這意味著,
操作系統(tǒng)必須在每次提出內(nèi)存請(qǐng)求時(shí)在用戶空間代碼和內(nèi)核代碼之間進(jìn)行切換。反復(fù)調(diào)用 malloc
或者
new
的程序,最終將由于不斷地進(jìn)行上下文切換而導(dǎo)致運(yùn)行緩慢。
對(duì)于在程序中分配的、并且以后不再需要使用的內(nèi)存,常常會(huì)忘記對(duì)其進(jìn)行刪除,并且 C/C++ 并沒有
提供自動(dòng)的垃圾收集。這將導(dǎo)致程序的內(nèi)存空間占用進(jìn)一步增長。在實(shí)際的大型程序中,性能將受到顯著
的影響,因?yàn)榭捎脙?nèi)存變得越來越少,并且硬盤訪問是非常耗時(shí)的。
設(shè)計(jì)目標(biāo)
您的內(nèi)存管理器應(yīng)該滿足下面的設(shè)計(jì)目標(biāo):
速度
與編譯器提供的分配器相比,內(nèi)存管理器的速度必須更快一些。重復(fù)的分配和釋放不應(yīng)該降低代碼的執(zhí)行
速度。如果可能的話,應(yīng)該對(duì)內(nèi)存管理器進(jìn)行優(yōu)化,以便處理代碼中頻繁出現(xiàn)的某些分配模式。
健壯性
在程序終止之前,內(nèi)存管理器必須歸還它向系統(tǒng)請(qǐng)求的所有內(nèi)存。也就是說,不應(yīng)該存在內(nèi)存泄漏。它還
應(yīng)該能夠處理錯(cuò)誤的情況(例如,請(qǐng)求過大的內(nèi)存),并且很好地解決這些問題。
用戶使用便利性
在將內(nèi)存管理器集成到他們的代碼中時(shí),用戶應(yīng)該只需要更改很少的代碼。
可移植性
應(yīng)該可以很容易地將內(nèi)存管理器移植到其它的系統(tǒng),并且不應(yīng)該使用與平臺(tái)相關(guān)的內(nèi)存管理特性。
創(chuàng)建內(nèi)存管理器的實(shí)用策略
在創(chuàng)建內(nèi)存管理器時(shí),下面的這些策略是很實(shí)用的:
- 請(qǐng)求較大的內(nèi)存塊。
- 對(duì)常見的請(qǐng)求大小進(jìn)行優(yōu)化。
- 在容器中收集刪除的內(nèi)存。
請(qǐng)求較大的內(nèi)存塊
最常見的內(nèi)存管理策略之一是,在程序啟動(dòng)期間請(qǐng)求一些較大的內(nèi)存塊,然后在代碼執(zhí)行期間反復(fù)地
使用它們。可以從這些內(nèi)存塊劃出部分內(nèi)存,以滿足各種數(shù)據(jù)結(jié)構(gòu)的內(nèi)存分配請(qǐng)求。這將極大地減少
系統(tǒng)調(diào)用的次數(shù),并提高執(zhí)行性能。
對(duì)常見的請(qǐng)求大小進(jìn)行優(yōu)化
在任何程序中,某些特定的請(qǐng)求大小將比其它大小的請(qǐng)求更加常見。如果對(duì)您的內(nèi)存管理器進(jìn)行優(yōu)化
以便更好地處理這些請(qǐng)求,那么它將工作得更加出色。
在容器中收集刪除的內(nèi)存
應(yīng)該將程序執(zhí)行期間刪除的內(nèi)存收集到容器中。然后,應(yīng)該使用這些容器來滿足進(jìn)一步的內(nèi)存請(qǐng)求。
如果某個(gè)請(qǐng)求失敗,那么應(yīng)該將內(nèi)存訪問委托給程序啟動(dòng)期間分配的某個(gè)較大的內(nèi)存塊。雖然內(nèi)存
管理最初用于加速程序的執(zhí)行和防止內(nèi)存泄漏,但這種技術(shù)可能會(huì)潛在地導(dǎo)致程序的較低內(nèi)存空間
占用,這是因?yàn)樗梢灾赜脛h除的內(nèi)存。這是編寫您自己的內(nèi)存分配器的另一個(gè)原因!
分析 C++ new/delete 操作符的執(zhí)行時(shí)間
我們將從一個(gè)簡單示例開始。假定您的代碼使用了一個(gè)稱為 Complex
的類(該類用于表示復(fù)數(shù)),
并且它使用了 new
和 delete
操作符所提供的機(jī)制,如清單 1 中所示。
class Complex
{
public:
Complex (double a, double b): r (a), c (b) {}
private:
double r; // Real Part
double c; // Complex Part
};
int main(int argc, char* argv[])
{
Complex* array[1000];
for (int i = 0;i < 5000; i++) {
for (int j = 0; j < 1000; j++) {
array[j] = new Complex (i, j);
}
for (int j = 0; j < 1000; j++) {
delete array[j];
}
}
return 0;
}
清單 1. Complex 類的 C++ 代碼
外層循環(huán)的每次迭代都會(huì)導(dǎo)致 1000 次分配和釋放。5000 次這樣的迭代將導(dǎo)致 10 百萬次用戶和
內(nèi)核代碼之間的切換。在 Solaris 10 計(jì)算機(jī)中使用 gcc-3.4.6 進(jìn)行編譯之后,執(zhí)行這個(gè)測(cè)試程序
平均需要花費(fèi) 3.5 秒。這是編譯器提供的全局 new
和 delete
操作符實(shí)現(xiàn)的基準(zhǔn)性能度量。要為
Complex
類創(chuàng)建自定義的內(nèi)存管理器以改進(jìn)編譯器的實(shí)現(xiàn),您需要重寫 Complex
類特定的 new
和
delete
操作符。
New/Delete:深入研究
在 C++ 中,對(duì)內(nèi)存管理進(jìn)行組織實(shí)際上就是重載 new
或者 delete
操作符。代碼中不同的類可能
需要使用不同的內(nèi)存分配策略,這意味著每個(gè)類需要特定的 new
。否則,必須重寫 new
或者 delete
全局操作符。可以采用這兩種形式中的任何一種來實(shí)現(xiàn)操作符重載,如清單 2 中所示。
清單 2. 重載 new 或者 delete 操作符void* operator new(size_t size);
void operator delete(void* pointerToDelete);
-OR-
void* operator new(size_t size, MemoryManager& memMgr);
void operator delete(void* pointerToDelete, MemoryManager& memMgr);
經(jīng)過重寫的 new
操作符負(fù)責(zé)分配第一個(gè)參數(shù)中指定大小的原始內(nèi)存,而 delete
操作符則負(fù)責(zé)釋放這
個(gè)內(nèi)存。請(qǐng)注意,這些例程只用于分配或者釋放內(nèi)存;它們并不會(huì)分別調(diào)用構(gòu)造函數(shù)或析構(gòu)函數(shù)。對(duì)
于由 new
操作符分配的內(nèi)存,將調(diào)用構(gòu)造函數(shù),而只有在調(diào)用了對(duì)象的析構(gòu)函數(shù)后才調(diào)用 delete
操
作符。
new
的第二種變體是 placement new
操作符,它接受一個(gè) MemoryManager
參數(shù),該參數(shù)基本上是為某
個(gè)對(duì)象分配原始內(nèi)存的數(shù)據(jù)結(jié)構(gòu),最后將調(diào)用這個(gè)對(duì)象的構(gòu)造函數(shù)。對(duì)于本教程,我們建議使用 new
或
者 delete
的第一種變體,因?yàn)楹笠环N變體將導(dǎo)致用戶代碼中 MemoryManager&
參數(shù)的大量增加,違反
了用戶使用便利性的設(shè)計(jì)目標(biāo)。
我們使用 MemoryManager
類,以便將 new
和 delete
操作符例程作為下面的 MemoryManager
例程的包裝,
從而進(jìn)行實(shí)際的內(nèi)存分配或者釋放,如清單 3 中所示: MemoryManager gMemoryManager;
// Memory Manager, global variable
。
清單 3. 作為包裝的 new、new[ ]、delete 和 delete[ ] 操作符MemoryManager gMemoryManager; // Memory Manager, global variable
void* operator new (size_t size)
{
return gMemoryManager.allocate(size);
}
void* operator new[ ] (size_t size)
{
return gMemoryManager.allocate(size);
}
void operator delete (void* pointerToDelete)
{
gMemoryManager.free(pointerToDelete);
}
void operator delete[ ] (void* arrayToDelete)
{
gMemoryManager.free(arrayToDelete);
}
注意:
- 傳遞給
new[ ]
操作符的 size 是數(shù)組每個(gè)元素的大小與數(shù)組元素個(gè)數(shù)相乘后所得的大小。 - 這個(gè)指針并不是在任何類特定的
new
、delete
、new[ ]
或者 delete[ ]
操作符中都可以
使用。實(shí)際上,這四個(gè)例程都用作靜態(tài)方法。您必須在設(shè)計(jì)過程中始終牢記這一點(diǎn)。 - 除了使用全局變量來聲明內(nèi)存管理器之外,您當(dāng)然還可以使用一個(gè)單例。
根據(jù)到目前為止的介紹,清單 4 顯示了內(nèi)存管理器類的基本接口。
清單 4. 內(nèi)存管理器接口
class IMemoryManager
{
public:
virtual void* allocate(size_t) = 0;
virtual void free(void*) = 0;
};
class MemoryManager : public IMemoryManager
{
public:
MemoryManager( );
virtual ~MemoryManager( );
virtual void* allocate(size_t);
virtual void free(void*);
};
我們還傾向于將 allocate
和 free
例程作為內(nèi)聯(lián)例程,以便實(shí)現(xiàn)更快的分派。
單線程環(huán)境中 Complex 類型的第一個(gè)內(nèi)存管理器
我們?cè)O(shè)計(jì)了本教程的第一個(gè)內(nèi)存管理器,并始終牢記到目前為止已經(jīng)介紹的那些原則。出于
簡單的考慮,這個(gè)自定義的內(nèi)存管理器專門用于 Complex
類型的對(duì)象,并且只能在單線程
環(huán)境中工作。其基本思想是,在可用的內(nèi)存管理器中保存 Complex
對(duì)象池,并通過這個(gè)池來
完成以后的分配工作。如果需要?jiǎng)?chuàng)建的 Complex
對(duì)象的數(shù)目超過了該池中對(duì)象的數(shù)目,那么
將對(duì)該池進(jìn)行擴(kuò)展。刪除的對(duì)象將歸還到這個(gè)池中。圖 1 很好地說明了所發(fā)生的事件。
圖 1. 創(chuàng)建 Complex 對(duì)象的池
該池中的每個(gè)塊具有兩個(gè)用途:
- 它存儲(chǔ)了一個(gè)
Complex
對(duì)象。 - 它必須能夠?qū)⑵渥陨磉B接到池中的下一個(gè)塊。
沒有選擇在 Complex
數(shù)據(jù)結(jié)構(gòu)中存儲(chǔ)一個(gè)指針,因?yàn)檫@將增加設(shè)計(jì)的整體內(nèi)存空間占用。相
比較而言,更好的方法是將 Complex
數(shù)據(jù)結(jié)構(gòu)的私有變量包裝到一個(gè)結(jié)構(gòu)中,并將其與 Complex
指針一起創(chuàng)建一個(gè)聯(lián)合。當(dāng)用作池中的一部分時(shí),這個(gè)指針用于指向該池中的下一個(gè)空閑元素。
當(dāng)用作獨(dú)立 Complex
對(duì)象時(shí),可以利用該結(jié)構(gòu)來保存實(shí)數(shù)和復(fù)數(shù)部分。清單 5 顯示了經(jīng)過修改的
數(shù)據(jù)結(jié)構(gòu)。
清單 5. 經(jīng)過修改的數(shù)據(jù)結(jié)構(gòu),可以在不產(chǎn)生額外開銷的情況下存儲(chǔ) Complex*
class Complex
{
public:
Complex (double a, double b): r (a), c (b) {}
private:
union {
struct {
double r; // Real Part
double c; // Complex Part
};
Complex* next;
};
};
然而,這個(gè)策略違反了用戶使用便利性的設(shè)計(jì)目標(biāo),因?yàn)樵诩蓛?nèi)存管理器的時(shí)候,我們希望在
原始數(shù)據(jù)結(jié)構(gòu)中進(jìn)行最小限度的更改。為了對(duì)此進(jìn)行改進(jìn),我們?cè)O(shè)計(jì)了一個(gè)新的 FreeStore
數(shù)據(jù)
結(jié)構(gòu),它是一個(gè)包裝數(shù)據(jù)結(jié)構(gòu),在保存為池中的一部分時(shí)用作指針,否則用作 Complex
對(duì)象。
清單 6 顯示了 FreeStore
對(duì)象的數(shù)據(jù)結(jié)構(gòu)。
清單 6. FreeStore 對(duì)象的數(shù)據(jù)結(jié)構(gòu)
struct FreeStore
{
FreeStore* next;
};
然后,該池成為 FreeStore
對(duì)象的鏈表,其中每一個(gè)都可以指向該池中的下一個(gè)元素,并且可以
用作一個(gè) Complex
對(duì)象。MemoryManager
類必須保存一個(gè)指針,以指向這個(gè)空閑池第一個(gè)元素的頭。
它應(yīng)該提供一些用于在需要時(shí)擴(kuò)展池大小的私有方法,以及在程序終止時(shí)清空內(nèi)存的方法。清單 7
包含了經(jīng)過修改的 Complex
類的數(shù)據(jù)結(jié)構(gòu),從而提供 FreeStore
功能。
清單 7. 經(jīng)過修改的 Complex 類數(shù)據(jù)結(jié)構(gòu),其中提供了 FreeStore 功能
#include <sys/types.h>
class MemoryManager: public IMemoryManager
{
struct FreeStore
{
FreeStore *next;
};
void expandPoolSize ();
void cleanUp ();
FreeStore* freeStoreHead;
public:
MemoryManager () {
freeStoreHead = 0;
expandPoolSize ();
}
virtual ~MemoryManager () {
cleanUp ();
}
virtual void* allocate(size_t);
virtual void free(void*);
};
MemoryManager gMemoryManager;
class Complex
{
public:
Complex (double a, double b): r (a), c (b) {}
inline void* operator new(size_t);
inline void operator delete(void*);
private:
double r; // Real Part
double c; // Complex Part
};
下面是用于內(nèi)存分配的偽代碼:
- 如果還沒有創(chuàng)建空閑存儲(chǔ),那么創(chuàng)建空閑存儲(chǔ),然后跳轉(zhuǎn)到步驟 3。
- 如果空閑存儲(chǔ)已經(jīng)耗盡,則創(chuàng)建一個(gè)新的空閑存儲(chǔ)。
- 返回空閑存儲(chǔ)的第一個(gè)元素,并且將空閑存儲(chǔ)的下一個(gè)元素標(biāo)記為空閑存儲(chǔ)的頭。
下面是用于內(nèi)存刪除的偽代碼:
- 讓刪除指針中的 next 域指向當(dāng)前空閑存儲(chǔ)的頭。
- 將刪除指針標(biāo)記為空閑存儲(chǔ)的頭。
清單 8 包含了 Complex
類的 new
和 delete
操作符的源代碼。清單 9 顯示了空閑存儲(chǔ)的擴(kuò)展和清理方法。
問題仍然存在。您能發(fā)現(xiàn)具體的問題嗎?
清單 8. 用于 Complex 類的自定義內(nèi)存分配或者釋放
inline void* MemoryManager::allocate(size_t size)
{
if (0 == freeStoreHead)
expandPoolSize ();
FreeStore* head = freeStoreHead;
freeStoreHead = head->next;
return head;
}
inline void MemoryManager::free(void* deleted)
{
FreeStore* head = static_cast <FreeStore*> (deleted);
head->next = freeStoreHead;
freeStoreHead = head;
}
void* Complex::operator new (size_t size)
{
return gMemoryManager.allocate(size);
}
void Complex::operator delete (void* pointerToDelete)
{
gMemoryManager.free(pointerToDelete);
}
空閑存儲(chǔ)的創(chuàng)建并不是那么簡單。關(guān)鍵在于理解相同的 FreeStore*
指針還可以用來表示 Complex
對(duì)象。
因此,單個(gè) FreeStore
指針?biāo)璧拇笮”仨毷?nbsp;FreeStore*
或者 Complex
中較大的一個(gè),如清單 9 中所示。 清單 9. 擴(kuò)展或者清空空閑存儲(chǔ)的代碼
#define POOLSIZE 32
void MemoryManager::expandPoolSize ()
{
size_t size = (sizeof(Complex) > sizeof(FreeStore*)) ?
sizeof(Complex) : sizeof(FreeStore*);
FreeStore* head = reinterpret_cast <FreeStore*> (new char[size]);
freeStoreHead = head;
for (int i = 0; i < POOLSIZE; i++) {
head->next = reinterpret_cast <FreeStore*> (new char [size]);
head = head->next;
}
head->next = 0;
}
void MemoryManager::cleanUp()
{
FreeStore* nextPtr = freeStoreHead;
for (; nextPtr; nextPtr = freeStoreHead) {
freeStoreHead = freeStoreHead->next;
delete [] nextPtr; // remember this was a char array
}
}
大功告成!您已經(jīng)為 Complex
類創(chuàng)建了自定義的內(nèi)存管理器。記得基準(zhǔn)測(cè)試花費(fèi)了 3.5 秒。編譯并
運(yùn)行相同測(cè)試(不需要在主例程中對(duì)客戶端代碼進(jìn)行任何更改),現(xiàn)在顯示執(zhí)行該程序只需要花費(fèi)
0.67 秒!為什么存在這樣顯著的性能改進(jìn)呢?主要有兩個(gè)原因:
- 在用戶和內(nèi)核代碼之間的切換數(shù)目明顯減少,因?yàn)樗ㄟ^將刪除的內(nèi)存收集回空閑存儲(chǔ)來重用這些內(nèi)存。
- 這個(gè)內(nèi)存管理器是一個(gè)適合單線程執(zhí)行的分配器。我們并不打算在多線程環(huán)境中運(yùn)行這些代碼。
之前,我們?cè)鴨柲欠癜l(fā)現(xiàn)了設(shè)計(jì)中的問題。這個(gè)問題就是,如果沒有刪除使用 new
創(chuàng)建的 Complex
對(duì)象,
那么在這種設(shè)計(jì)中就沒有辦法回收丟失的內(nèi)存。如果開發(fā)人員使用編譯器提供的 new
和 delete
全局操作符,
那么情況也是相同的。然而,內(nèi)存管理器不僅僅涉及性能改進(jìn),它還應(yīng)該能夠在設(shè)計(jì)中避免內(nèi)存泄漏。對(duì)于
使用 new
創(chuàng)建的 Complex
對(duì)象進(jìn)行顯式刪除的情況,cleanUp
例程僅僅將內(nèi)存返回給操作系統(tǒng)。這個(gè)問題只
能通過從操作系統(tǒng)中請(qǐng)求更大的內(nèi)存塊(在程序初始化期間)、并對(duì)它們進(jìn)行存儲(chǔ)來加以解決。使用 Complex
的 new
操作符從這些塊中劃出部分內(nèi)存來響應(yīng)內(nèi)存的請(qǐng)求。cleanUp
例程在釋放這些塊時(shí)應(yīng)該將其作為一個(gè)整
體,而不是從這些內(nèi)存塊中創(chuàng)建的單個(gè)對(duì)象。
有趣的說明
仍然使用全局 new
和 delete
操作符創(chuàng)建空閑存儲(chǔ)的元素。然而,您也可以使用 malloc
和 free
的組合。在這
兩種情況下,執(zhí)行的平均時(shí)間基本相同。
位圖內(nèi)存管理器
可以在最初的固定大小內(nèi)存分配方案基礎(chǔ)上進(jìn)行有趣的改進(jìn),即位圖內(nèi)存管理器。在這個(gè)方案中,向操作系統(tǒng)較
大的塊進(jìn)行內(nèi)存請(qǐng)求,并且通過從這些塊劃出部分內(nèi)存來實(shí)現(xiàn)內(nèi)存的分配。通過將整個(gè)塊作為一個(gè)整體來完成釋
放操作,因而避免了任何潛在的泄漏。這種方法的另一個(gè)優(yōu)點(diǎn)是,它可以支持 new
和 delete
操作的數(shù)組版本。
在這種方法中,內(nèi)存管理器將請(qǐng)求較大的內(nèi)存塊。然后進(jìn)一步將這個(gè)塊劃分為較小的、且大小固定的內(nèi)存塊。在
這種情況下,每個(gè)內(nèi)存塊的大小都與 Complex
對(duì)象的大小相等。根據(jù)這些塊中內(nèi)存塊的數(shù)目,單獨(dú)地維護(hù)一個(gè)位
圖,以顯示每個(gè)塊的狀態(tài)(空閑、或者已分配),并且位的數(shù)目與內(nèi)存塊的數(shù)目相等。當(dāng)程序請(qǐng)求一個(gè)新的
Complex
對(duì)象時(shí),內(nèi)存管理器將根據(jù)位圖來獲得一個(gè)空閑塊。對(duì)于所有的空閑塊,將其對(duì)應(yīng)的位設(shè)置為 1。對(duì)于
已使用的塊,則將它們對(duì)應(yīng)的位設(shè)置為 0。要提供 new[ ]
和 delete[ ]
操作符,您需要維護(hù)一個(gè)輔助的數(shù)據(jù)結(jié)構(gòu),
以便在創(chuàng)建或者銷毀 Complex
數(shù)組對(duì)象時(shí)幫助跟蹤位圖中有多少位需要設(shè)置為 1 或者 0。
創(chuàng)建一個(gè)位圖內(nèi)存管理器
內(nèi)存管理器從操作系統(tǒng)中請(qǐng)求足夠大的內(nèi)存塊。從這個(gè)塊中劃分出部分內(nèi)存以進(jìn)行稍后的內(nèi)存分配。如果請(qǐng)求失敗,
那么內(nèi)存管理器將解決這個(gè)問題,并將合適的消息傳遞給用戶,表示它已經(jīng)退出。在這個(gè)階段中,位圖中的所有條
目都設(shè)置為 1。
如果內(nèi)存管理器耗盡了空閑的內(nèi)存塊,那么它將向操作系統(tǒng)進(jìn)一步請(qǐng)求較大的內(nèi)存塊。MemoryManager
數(shù)據(jù)結(jié)構(gòu)中的
每個(gè)位圖都可以用于增大對(duì)應(yīng)的內(nèi)存塊。然而,在調(diào)用刪除操作之后,用戶可以使用對(duì)應(yīng)的塊。因此,非連續(xù)的刪除
調(diào)用將導(dǎo)致所謂的分段的內(nèi)存,并且可以從這個(gè)內(nèi)存中提供適當(dāng)大小的塊。
清單 10 中給出了 MemoryManager
類的基本結(jié)構(gòu)。它包含 MemoryPoolList
,后者保存了從操作系統(tǒng)中請(qǐng)求的內(nèi)存塊的
起始地址。對(duì)于每個(gè)塊,在 BitMapEntryList
中都存在一個(gè)相應(yīng)的條目。FreeMapEntries
是一種數(shù)據(jù)結(jié)構(gòu),可以用來
為 new
調(diào)用的非數(shù)組版本提高下一個(gè)可用空閑塊的搜索速度。
清單 10. MemoryManager 類的定義class MemoryManager : public IMemoryManager
{
std::vector<void*> MemoryPoolList;
std::vector<BitMapEntry> BitMapEntryList;
//the above two lists will maintain one-to-one correspondence and hence
//should be of same size.
std::set<BitMapEntry*> FreeMapEntries;
std::map<void*, ArrayMemoryInfo> ArrayMemoryList;
private:
void* AllocateArrayMemory(size_t size);
void* AllocateChunkAndInitBitMap();
void SetBlockBit(void* object, bool flag);
void SetMultipleBlockBits(ArrayMemoryInfo* info, bool flag);
public:
MemoryManager( ) {}
~MemoryManager( ) {}
void* allocate(size_t);
void free(void*);
std::vector<void*> GetMemoryPoolList();
};
ArrayMemoryList
保存了為 Complex
對(duì)象數(shù)組所分配的內(nèi)存的相關(guān)信息。實(shí)際上,它是數(shù)組的起始地址到結(jié)構(gòu)的映射,
以便維護(hù)MemPoolListIndex
、位圖中數(shù)組的 StartPosition
和數(shù)組的大小,如清單 11 中所示。
清單 11. ArrayInfo 結(jié)構(gòu)定義
typedef struct ArrayInfo
{
int MemPoolListIndex;
int StartPosition;
int Size;
}
ArrayMemoryInfo;
清單 12. BitMapEntry 類定義
typedef struct BitMapEntry
{
int Index;
int BlocksAvailable;
int BitMap[BIT_MAP_SIZE];
public:
BitMapEntry():BlocksAvailable(BIT_MAP_SIZE)
{
memset(BitMap,0xff,BIT_MAP_SIZE / sizeof(char));
// initially all blocks are free and bit value 1 in the map denotes
// available block
}
void SetBit(int position, bool flag);
void SetMultipleBits(int position, bool flag, int count);
void SetRangeOfInt(int* element, int msb, int lsb, bool flag);
Complex* FirstFreeBlock(size_t size);
Complex* ComplexObjectAddress(int pos);
void* Head();
}
BitMapEntry;
內(nèi)存管理器將初始化一個(gè)“n”位的數(shù)組(作為位圖),這樣一來,這個(gè)數(shù)組中的每個(gè)位對(duì)應(yīng)于所請(qǐng)求的內(nèi)存的一個(gè)
塊。將所有的條目都設(shè)置為 1。這項(xiàng)工作在 BitMapEntry
的構(gòu)造函數(shù)中完成。
在對(duì) Complex
類的 new
或者 malloc
的非數(shù)組版本進(jìn)行調(diào)用的過程中,內(nèi)存管理器將檢查 FreeMapEntries
數(shù)據(jù)結(jié)構(gòu)
中是否存在可用的塊。如果在其中找到了可用的塊,則返回這個(gè)塊,否則從操作系統(tǒng)中請(qǐng)求一個(gè)新的內(nèi)存塊,并設(shè)
置其對(duì)應(yīng)的 BitMapEntry
。從這個(gè)塊中劃出部分內(nèi)存塊返回給用戶,將其可用位設(shè)置為 0 以表示它不再空閑,并且
將指針返回給用戶,清單 13 中顯示了相關(guān)的代碼。
清單 13. MemoryManager::allocate 定義
void* MemoryManager::allocate(size_t size)
{
if(size == sizeof(Complex)) // mon-array version
{
std::set<BitMapEntry*>::iterator freeMapI = FreeMapEntries.begin();
if(freeMapI != FreeMapEntries.end())
{
BitMapEntry* mapEntry = *freeMapI;
return mapEntry->FirstFreeBlock(size);
}
else
{
AllocateChunkAndInitBitMap();
FreeMapEntries.insert(&(BitMapEntryList[BitMapEntryList.size() - 1]));
return BitMapEntryList[BitMapEntryList.size() - 1].FirstFreeBlock(size);
}
}
else // array version
{
if(ArrayMemoryList.empty())
{
return AllocateArrayMemory(size);
}
else
{
std::map<void*, ArrayMemoryInfo>::iterator infoI =ArrayMemoryList.begin();
std::map<void*, ArrayMemoryInfo>::iterator infoEndI =
ArrayMemoryList.end();
while(infoI != infoEndI)
{
ArrayMemoryInfo info = (*infoI).second;
if(info.StartPosition != 0) // search only in those mem blocks
continue; // where allocation is done from first byte
else
{
BitMapEntry* entry = &BitMapEntryList[info.MemPoolListIndex];
if(entry->BlocksAvailable < (size / sizeof(Complex)))
return AllocateArrayMemory(size);
else
{
info.StartPosition = BIT_MAP_SIZE - entry->BlocksAvailable;
info.Size = size / sizeof(Complex);
Complex* baseAddress = static_cast<Complex*>(
MemoryPoolList[info.MemPoolListIndex]) + info.StartPosition;
ArrayMemoryList[baseAddress] = info;
SetMultipleBlockBits(&info, false);
return baseAddress;
}
}
}
}
}
}
清單 14 中給出了為單個(gè)塊設(shè)置或重置位的代碼。
清單 14. MemoryManager::SetBlockBit 定義
void MemoryManager::SetBlockBit(void* object, bool flag)
{
int i = BitMapEntryList.size() - 1;
for (; i >= 0 ; i--)
{
BitMapEntry* bitMap = &BitMapEntryList[i];
if((bitMap->Head <= object )&&
(&(static_cast<Complex*>(bitMap->Head))[BIT_MAP_SIZE-1] >= object))
{
int position = static_cast<Complex*>(object) -
static_cast<Complex*>(bitMap->Head);
bitMap->SetBit(position, flag);
flag ? bitMap->BlocksAvailable++ : bitMap->BlocksAvailable--;
}
}
}
清單 15 中給出了為多個(gè)塊設(shè)置或重置位的代碼。
清單 15. MemoryManager::SetMultipleBlockBit 定義
void MemoryManager::SetMultipleBlockBits(ArrayMemoryInfo* info, bool flag)
{
BitMapEntry* mapEntry = &BitMapEntryList[info->MemPoolListIndex];
mapEntry->SetMultipleBits(info->StartPosition, flag, info->Size);
}
對(duì)于數(shù)組版本而言,處理方式有一點(diǎn)不同。用于數(shù)組的內(nèi)存塊與那些用于單個(gè)對(duì)象內(nèi)存分配的
內(nèi)存塊分配自不同的內(nèi)存塊。這減少了在調(diào)用兩種類型的 new
時(shí)搜索下一個(gè)空閑可用內(nèi)存的開銷。
內(nèi)存管理器將查找 ArrayMemoryList
數(shù)據(jù)結(jié)構(gòu),以獲取為數(shù)組提供內(nèi)存的塊的有關(guān)信息。當(dāng)找到
時(shí),它將獲得這個(gè)塊中的適當(dāng)部分,將其地址提供給用戶,并將相應(yīng)的位設(shè)置為 0。如果出于某
種原因而導(dǎo)致操作失敗,那么將從操作系統(tǒng)中請(qǐng)求一個(gè)新的塊,并從中劃分出部分內(nèi)存。兩種
new 調(diào)用的內(nèi)存塊作為 MemPoolList
的一部分存儲(chǔ)在 MemoryManager
類中。
在調(diào)用 Complex
類型對(duì)象的指針的 delete
、或者 free
的非數(shù)組版本的過程中,首先確定包含這
個(gè)指針的內(nèi)存塊(請(qǐng)參見清單 16)。然后,根據(jù)這個(gè)塊的 BitMapEntry
將對(duì)應(yīng)的位重置為 1,
從而使其成為可用的塊。整個(gè)操作將花費(fèi) O(1) 時(shí)間。對(duì)于 delete []
,從 ArrayMemoryList
中
檢索相關(guān)信息,并且在已知起始位置和位圖大小的情況下,將這些位標(biāo)記為可用。
清單 16. MemoryManager::free 定義
void MemoryManager::free(void* object)
{
if(ArrayMemoryList.find(object) == ArrayMemoryList.end())
SetBlockBit(object, true); // simple block deletion
else
{//memory block deletion
ArrayMemoryInfo *info = &ArrayMemoryList[object];
SetMultipleBlockBits(info, true);
}
}
下載部分中提供了位圖內(nèi)存管理器代碼的完整清單。它是這部分討論的內(nèi)存管理器的一個(gè)
工作示例,并且包括了我們?cè)谶@個(gè)部分中討論的所有清單。
繼承帶來的問題
派生類的大小很可能會(huì)與其基類有很大的不同。例如,可以考慮 Complex
類的一個(gè)派生變體,
稱為 ComplexT
,它用于跟蹤復(fù)數(shù)的值隨時(shí)間變化的情況,其中包含一個(gè)附加的 unsigned long
用來存儲(chǔ)時(shí)間。先前重寫的 new
或者 delete
操作符現(xiàn)在將無法工作,除非我們專門針對(duì)這個(gè)
派生變體再次定義它們。對(duì)于這個(gè)問題,有三種可能的解決方案:
使用 MemoryManager
為這兩個(gè)不同的類維護(hù)兩個(gè)不同的池。實(shí)際上,這意味著分配/釋
放例程的兩個(gè)變體,以及用來存儲(chǔ)這兩個(gè)池的頭的兩個(gè)指針。這種方式可能奏效,但
可伸縮性受到限制。
根據(jù)最大的派生類大小維護(hù)一個(gè)池。allocate
例程始終返回最大派生類的大小,無論
是基類、派生類層次中的哪個(gè)對(duì)象在請(qǐng)求內(nèi)存。這樣可以很好地解決問題,但它并不是
一個(gè)非常有價(jià)值的方法,因?yàn)槌绦虻膬?nèi)存空間占用將會(huì)增加。
為可變大小的分配創(chuàng)建通用的內(nèi)存管理器。
基于空閑列表的內(nèi)存管理器
在一個(gè)典型的程序中,內(nèi)存塊的大多數(shù)請(qǐng)求都是特定大小的。這個(gè)內(nèi)存管理器將充分利用這個(gè)
特點(diǎn)。要實(shí)現(xiàn)這一點(diǎn),您需要維護(hù)一些列表,其中包含這種大小的空閑塊的地址。在技術(shù)行話
中,我們稱這些列表為空閑列表。例如,可以考慮某個(gè)程序,它將提出 8、16、24、32、48
和 64 字節(jié)的內(nèi)存請(qǐng)求。出于這個(gè)目的,內(nèi)存管理器中包含六個(gè)不同的空閑列表,并請(qǐng)求從對(duì)
應(yīng)的列表提供特定大小的內(nèi)存。與位圖內(nèi)存管理器類似,在啟動(dòng)時(shí)從操作系統(tǒng)中請(qǐng)求內(nèi)存,并
且內(nèi)存管理器在內(nèi)部將從操作系統(tǒng)請(qǐng)求的塊劃分到各個(gè)列表中。在用戶為某個(gè)對(duì)象請(qǐng)求內(nèi)存時(shí),
比如“m”個(gè)字節(jié),那么會(huì)將對(duì)象的大小轉(zhuǎn)換為最接近的塊大小“n”,并且存在一個(gè)空閑列表用來
存儲(chǔ)大小為“n”的塊。
有關(guān)保護(hù)字節(jié)(guard bytes)的簡單介紹
“n”字節(jié)的塊不僅存儲(chǔ)了對(duì)象,還存儲(chǔ)了一些元數(shù)據(jù)信息。每個(gè)塊的末尾附近都包含四個(gè)額外的
字節(jié),其中包含在堆內(nèi)存操作范例中稱為保護(hù)字節(jié) 的內(nèi)容。通常,memcpy
和 memset
函數(shù)可以
訪問所分配內(nèi)存的界限之外的內(nèi)存字節(jié)。這將導(dǎo)致堆內(nèi)存的破壞。許多編譯器都為所分配的 mem
塊添加了一些特殊字符,以充當(dāng)該塊的哨兵(或者保護(hù)字節(jié))。對(duì)于所分配內(nèi)存的這些塊所進(jìn)行
的任何訪問,都將檢查所分配的塊附近的特殊字符。如果找不到這些字符,那么將認(rèn)為在程序執(zhí)
行期間以非法的方式更改了它們的值,這暗示堆內(nèi)存不再處于有序的狀態(tài),并且因此受到了破壞。
通過這種機(jī)制,程序員可以捕獲一些所了解的內(nèi)存錯(cuò)誤。在下面的示例中,這四個(gè)字節(jié)中的其中
兩個(gè)充當(dāng)了保護(hù)字節(jié),第三個(gè)字節(jié)用于存儲(chǔ)這個(gè)對(duì)象的大小,最后一個(gè)字節(jié)存儲(chǔ)了一個(gè)標(biāo)志,表
示這個(gè)對(duì)象是否可用、或者已經(jīng)被占用。
創(chuàng)建一個(gè)空閑列表內(nèi)存管理器
如前所述,內(nèi)存管理器維護(hù)了一些指針的列表,這些指針指向可變大小的塊。它還對(duì)從操作系統(tǒng)
請(qǐng)求而來的內(nèi)存塊進(jìn)行維護(hù),并作為memoryPool
。在 MemoryManager
調(diào)用其析構(gòu)函數(shù)時(shí),這個(gè)池
可以用于釋放整個(gè)內(nèi)存塊。清單 17 顯示了該數(shù)據(jù)結(jié)構(gòu)。
清單 17. 基于空閑列表實(shí)現(xiàn)的 MemoryManager 類定義
class MemoryManager : public IMemoryManager
{
private:
VoidPtrList Byte8PtrList;
VoidPtrList Byte16PtrList;
VoidPtrList Byte24PtrList;
VoidPtrList Byte32PtrList;
VoidPtrList Byte40PtrList;
std::vector<void*> MemoryPoolList;
. . . . . . . . . //helper routines may go in here
public:
MemoryManager( ) {}
~MemoryManager( ) {}
void* allocate(size_t);
void free(void*);
};
我們的示例使用了大小為 16、28 和 32 字節(jié)的三個(gè)類,因而需要大小為 24、32和40
個(gè)字節(jié)的塊來保存它們的保護(hù)字節(jié)。因此,Byte24PtrList
、Byte32PtrList
和
Byte40PtrList
的使用最為頻繁,并且代碼也主要是和它們相關(guān)的。然而,設(shè)計(jì)相當(dāng)
靈活,也可以用于添加您所希望的其他大小的列表。
為這些類重載了操作符 new
和 delete
,它們將調(diào)用委派給負(fù)責(zé)內(nèi)存分配和釋放的
allocate
和 free
例程。下面將詳細(xì)地介紹這些例程。我們使用了大小為 1024 的
塊作為對(duì)象的初始計(jì)數(shù)。另外,將示例中使用的每個(gè)類的大小作為預(yù)定義的常數(shù)進(jìn)
行了存儲(chǔ),如清單 18 中所示。
清單 18. 這個(gè)實(shí)現(xiàn)中所使用的預(yù)定義常數(shù)
const int JOB_SCHEDULER_SIZE = sizeof(JobScheduler);
const int COMPLEX_SIZE = sizeof(Complex);
const int COORDINATE_SIZE = sizeof(Coordinate);
const int POOL_SIZE = 1024; //number of elements in a single pool
//can be chosen based on application requirements
const int MAX_BLOCK_SIZE = 36; //depending on the application it may change
//In above case it came as 36
在 allocate
和 free
例程中使用了這些常數(shù)。
allocate 例程
對(duì)于給定大小的塊,allocate
例程將確定使用內(nèi)存管理器中的哪個(gè)列表。它將查找
必需的列表,以查看是否存在可用的塊。請(qǐng)注意,這個(gè)列表僅存儲(chǔ)了指向該塊的指
針,而不是塊本身(該塊是 MemoryPoolList
的一部分)。
如果空閑列表是空的,那么將從操作系統(tǒng)中請(qǐng)求附加的內(nèi)存塊,并由
InitialiseByte24List
、InitialiseByte32List
和InitialiseByte40List
例程將其組織為分區(qū)塊。
當(dāng)存在某個(gè)空閑塊地址可供使用時(shí),將對(duì)應(yīng)的塊標(biāo)記為不可使用,設(shè)置其保護(hù)字節(jié),
并且返回該塊的地址。可以考慮如清單 19 中所示的實(shí)現(xiàn)。
清單 19. MemoryManager::allocate 定義
void* MemoryManager::allocate(size_t size)
{
void* base = 0;
switch(size)
{
case JOB_SCHEDULER_SIZE :
{
if(Byte32PtrList.empty())
{
base = new char [32 * POOL_SIZE];
MemoryPoolList.push_back(base);
InitialiseByte32List(base);
}
void* blockPtr = Byte32PtrList.front();
*((static_cast<char*>(blockPtr)) + 30) = 32; //size of block set
*((static_cast<char*>(blockPtr)) + 31) = 0; //block is no longer free
Byte32PtrList.pop_front();
return blockPtr;
}
case COORDINATE_SIZE :
{
if(Byte40PtrList.empty())
{
base = new char [40 * POOL_SIZE];
MemoryPoolList.push_back(base);
InitialiseByte40List(base);
}
void* blockPtr = Byte40PtrList.front();
*((static_cast<char*>(blockPtr)) + 38) = 40; //size of block set
*((static_cast<char*>(blockPtr)) + 39) = 0; //block is no longer free
Byte40PtrList.pop_front();
return blockPtr;
}
case COMPLEX_SIZE :
{
if(Byte24PtrList.empty())
{
base = new char [24 * POOL_SIZE];
MemoryPoolList.push_back(base);
InitialiseByte24List(base);
}
void* blockPtr = Byte24PtrList.front();
*((static_cast<char*>(blockPtr)) + 22) = 24; //size of block set
*((static_cast<char*>(blockPtr)) + 23) = 0; //block is no longer free
Byte24PtrList.pop_front();
return blockPtr;
}
default : break;
}
return 0;
}
free 例程
free
例程接收塊的地址,并搜索位于塊末尾的、包含大小信息的字節(jié)(在保護(hù)字節(jié)中)。它是
塊中的倒數(shù)第二個(gè)字節(jié)。在獲得這個(gè)大小之后,將對(duì)象標(biāo)記為可用(將最后一個(gè)字節(jié)設(shè)置為 1),
并獲取對(duì)應(yīng)的列表。然后,將該對(duì)象加入空閑 列表中,這也標(biāo)志著完成了刪除操作。可以考慮
清單 20 中的實(shí)現(xiàn)。
清單 20. MemoryManager::free 定義
void MemoryManager::free(void* object)
{
char* init = static_cast<char*>(object);
while(1)
{
int count = 0;
while(*init != static_cast<char>(0xde))
//this loop shall never iterate more than
{ // MAX_BLOCK_SIZE times and hence is O(1)
init++;
if(count > MAX_BLOCK_SIZE)
{
printf ("runtime heap memory corruption near %d", object);
exit(1);
}
count++;
}
if(*(++init) == static_cast<char>(0xad)) // we have hit the guard bytes
break; // from the outer while
}
init++;
int blockSize = static_cast<int>(*init);
switch(blockSize)
{
case 24: Byte24PtrList.push_front(object); break;
case 32: Byte32PtrList.push_front(object); break;
case 40: Byte40PtrList.push_front(object); break;
default: break;
}
init++;
*init = 1; // set free/available byte
}
下載部分中提供了空閑列表內(nèi)存管理器代碼的完整清單。它是空閑列表內(nèi)存管理器實(shí)現(xiàn)的一個(gè)
工作示例,并且包括我們?cè)谶@個(gè)部分中討論的所有清單。
優(yōu)點(diǎn)和不足之處
這個(gè)設(shè)計(jì)有下面幾個(gè)優(yōu)點(diǎn):
雖然這個(gè)設(shè)計(jì)可以顯著地改善性能,但它仍然存在不足之處,即它需要大量的內(nèi)存。
多線程內(nèi)存管理器
到目前為止,我們所創(chuàng)建的內(nèi)存管理器并沒有考慮到并發(fā)的環(huán)境。在多線程環(huán)境中,
可能會(huì)有多個(gè)線程同時(shí)嘗試進(jìn)行內(nèi)存的分配和釋放。這意味著,我們必須確保在內(nèi)
存管理器中進(jìn)行的分配和釋放操作都是具有原子性的。也就是說,在兩個(gè)線程同時(shí)
嘗試這些操作時(shí),我們必須在這兩個(gè)線程之間提供一種互斥的機(jī)制。要確保分配和
釋放操作具有原子性,標(biāo)準(zhǔn)的方法是使用一種基于鎖的機(jī)制。當(dāng)一個(gè)線程調(diào)用這兩
種方法其中之一時(shí),在它實(shí)際獲得對(duì)內(nèi)存的訪問或者釋放內(nèi)存之前,必須獲得一個(gè)
獨(dú)占鎖。如果該鎖由另一個(gè)線程所擁有,那么將阻塞當(dāng)前線程,直到它擁有這個(gè)鎖
為止。清單 21 說明了用作鎖定機(jī)制的函數(shù)簽名。
清單 21. pthread mutex 方法
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t *mutex,
const pthread_mutexattr_t *attr);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
就本文而言,我們使用了標(biāo)準(zhǔn) Header pthread.h
中定義的 pthread mutexes,GNU
編譯器在安裝時(shí)就提供了這個(gè) Header,以便模擬鎖定或者解鎖機(jī)制。allocation
和
free
例程中的代碼位于 pthread_mutex_lock
和 pthread_mutex_unlock
方法之間。
如果某個(gè)線程正在訪問內(nèi)存池時(shí),又有另一個(gè)線程調(diào)用了這些例程其中之一,那么它
必須等待,直到該鎖可用為止。清單 22 是該方法的經(jīng)過修改的代碼片段。
清單 22. 分配和釋放方法的并發(fā)版本
void* MemoryManager::allocate(size_t size)
{
pthread_mutex_lock (&lock);
... // usual memory allocation code
pthread_mutex_unlock (&lock);
}
void* MemoryManager::free(size_t size)
{
pthread_mutex_lock (&lock);
... // usual memory deallocation code
pthread_mutex_unlock (&lock);
}
現(xiàn)在,內(nèi)存管理器類需要包括一個(gè) pthread_mutex_lock
對(duì)象。需要修改類構(gòu)造函數(shù),
以便調(diào)用 pthread_mutex_init
初始化鎖,并且類析構(gòu)函數(shù)必須調(diào)用 pthread_mutex_destroy
以銷毀 mutex 對(duì)象。清單 23 是 MemoryManager
類的修改之后的代碼。
清單 23. 經(jīng)過修改的 MemoryManager 類,以處理多線程的分配和釋放
class MemoryManager : public IMemoryManager
{
pthread_mutex_t lock;
... // usual MemoryManager code follows
};
MemoryManager::MemoryManager ()
{
pthread_mutex_init (&lock, NULL);
... // usual constructor code
}
MemoryManager:: ~MemoryManager ()
{
... // usual destructor code
pthread_mutex_destroy (&lock);
}
現(xiàn)在,運(yùn)行內(nèi)存管理器的多線程版本。在我們的測(cè)試中,與單線程版本相比,它的運(yùn)行要慢
得多,這說明了為什么需要提供專用的、自定義的內(nèi)存管理器。
總結(jié)
本教程解釋了下面的幾個(gè)概念:
- 在用戶代碼中使用內(nèi)存管理器的需求。
- 內(nèi)存管理器的設(shè)計(jì)需求。
- 如何創(chuàng)建固定大小的分配器或者內(nèi)存管理器。
- 如何創(chuàng)建可變大小的分配器。
- 多線程環(huán)境中的內(nèi)存分配。
有關(guān)這個(gè)主題的更多信息,請(qǐng)參見參考資料部分。
下載
描述 | 名字 | 大小 | 下載方法 |
---|
Source files for bitmapped memory manager | bitmapped_memory_manager.zip | 4KB | HTTP |
Source files for free-lists based memory manager | free_list_mem_mgr.zip | 3KB | HTTP |
參考資料
學(xué)習(xí)
from:
http://www.ibm.com/developerworks/cn/education/aix/au-memorymanager/index.html
posted on 2012-05-26 22:41
chatler 閱讀(2540)
評(píng)論(0) 編輯 收藏 引用 所屬分類:
OS