• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 297,  comments - 15,  trackbacks - 0
            Arpan Sen, 技術(shù)負(fù)責(zé)人, Synapti Computer Aided Design Pvt Ltd
            Rahul Kumar Kardam (rahul@syncad.com), 高級(jí)軟件工程師, 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í)有基本的了解。此外,還需要了解 malloccallocfreememcpy 和 
            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ù)mallocfreecalloc 和 realloc,以及 C++ 中的 newnew [ ]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è)指針并不是在任何類特定的 newdeletenew[ ] 或者 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)存分配的偽代碼:

            1. 如果還沒有創(chuàng)建空閑存儲(chǔ),那么創(chuàng)建空閑存儲(chǔ),然后跳轉(zhuǎn)到步驟 3。
            2. 如果空閑存儲(chǔ)已經(jīng)耗盡,則創(chuàng)建一個(gè)新的空閑存儲(chǔ)。
            3. 返回空閑存儲(chǔ)的第一個(gè)元素,并且將空閑存儲(chǔ)的下一個(gè)元素標(biāo)記為空閑存儲(chǔ)的頭。

            下面是用于內(nèi)存刪除的偽代碼:

            1. 讓刪除指針中的 next 域指向當(dāng)前空閑存儲(chǔ)的頭。
            2. 將刪除指針標(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; 

            為了簡化位圖的操作,將其作為存儲(chǔ)某些額外元數(shù)據(jù)信息的 BitMapEntry 對(duì)象來進(jìn)行維護(hù),如
            清單 12 中所示。要在
            位圖中設(shè)置一個(gè)或者多個(gè)位,可以使用 
            SetBit 和 SetMultipleBits 應(yīng)用程序接口 (API)。FirstFreeBlock() API 可以
            檢索位圖所指向的第一個(gè)空閑塊。

            清單 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 )&amp;&amp; 
                  (&(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é)。因此,Byte24PtrListByte32PtrList 和
             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)存塊,并由
            InitialiseByte24ListInitialiseByte32List 和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):

            • 可以很好地處理可變大小的內(nèi)存分配

            • 設(shè)計(jì)靈活,并且可以通過確定每個(gè)軟件所需的列表大小來進(jìn)行擴(kuò)展

            • 添加保護(hù)字節(jié)的能力允許為對(duì)象存儲(chǔ)大量元數(shù)據(jù)信息。我們已經(jīng)將它
              用于確定運(yùn)行時(shí)堆內(nèi)存的破壞情況

            雖然這個(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 mutexesGNU
            編譯器在安裝時(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 managerbitmapped_memory_manager.zip4KBHTTP
            Source files for free-lists based memory managerfree_list_mem_mgr.zip3KBHTTP


            參考資料

            學(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
            <2009年7月>
            2829301234
            567891011
            12131415161718
            19202122232425
            2627282930311
            2345678

            常用鏈接

            留言簿(10)

            隨筆分類(307)

            隨筆檔案(297)

            algorithm

            Books_Free_Online

            C++

            database

            Linux

            Linux shell

            linux socket

            misce

            • cloudward
            • 感覺這個(gè)博客還是不錯(cuò),雖然做的東西和我不大相關(guān),覺得看看還是有好處的

            network

            OSS

            • Google Android
            • Android is a software stack for mobile devices that includes an operating system, middleware and key applications. This early look at the Android SDK provides the tools and APIs necessary to begin developing applications on the Android platform using the Java programming language.
            • os161 file list

            overall

            搜索

            •  

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            人妻久久久一区二区三区| 国产午夜精品久久久久免费视| yellow中文字幕久久网| 99久久99这里只有免费的精品| 久久ZYZ资源站无码中文动漫| 久久久av波多野一区二区| .精品久久久麻豆国产精品| 久久99精品国产麻豆| 久久精品国内一区二区三区| 国内精品久久久久久久涩爱| 中文字幕精品久久久久人妻| 一本色道久久综合狠狠躁| 精品无码久久久久久尤物| 91久久精品国产91性色也| 亚洲人成无码网站久久99热国产| av色综合久久天堂av色综合在| 久久国产精品成人影院| 人人狠狠综合久久亚洲88| 久久婷婷五月综合色99啪ak| 精品人妻伦九区久久AAA片69| 精品久久人妻av中文字幕| 久久久精品久久久久特色影视| 97久久国产露脸精品国产| 精品久久久久久国产| 久久久久亚洲av毛片大| 亚洲国产精品久久电影欧美| 99热成人精品热久久669| 久久精品人妻一区二区三区| 久久综合狠狠综合久久| 51久久夜色精品国产| 国产精品成人久久久| 久久久综合九色合综国产| 亚洲天堂久久久| 久久综合综合久久97色| 77777亚洲午夜久久多人| 91麻豆精品国产91久久久久久| 久久热这里只有精品在线观看| 国内精品久久国产大陆| 久久人与动人物a级毛片| 99久久精品无码一区二区毛片 | 久久亚洲熟女cc98cm|