• <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>
            摘要: 本文主要對(duì)Windows內(nèi)存管理中的堆管理技術(shù)進(jìn)行討論,并簡(jiǎn)要介紹了堆的創(chuàng)建、內(nèi)存塊的分配與再分配、堆的撤銷以及new和delete操作符的使用等內(nèi)容。

              關(guān)鍵詞: 堆;堆管理
              1 引言

              在大多數(shù)Windows應(yīng)用程序設(shè)計(jì)中,都幾乎不可避免的要對(duì)內(nèi)存進(jìn)行操作和管理。在進(jìn)行大尺寸內(nèi)存的動(dòng)態(tài)分配時(shí)尤其顯的重要。本文即主要對(duì)內(nèi)存管理中的堆管理技術(shù)進(jìn)行論述。

              堆(Heap)實(shí)際是位于保留的虛擬地址空間中的一個(gè)區(qū)域。剛開(kāi)始時(shí),保留區(qū)域中的多數(shù)頁(yè)面并沒(méi)有被提交物理存儲(chǔ)器。隨著從堆中越來(lái)越多的進(jìn)行內(nèi)存分配,堆管理器將逐漸把更多的物理存儲(chǔ)器提交給堆。堆的物理存儲(chǔ)器從系統(tǒng)頁(yè)文件中分配,在釋放時(shí)有專門的堆管理器負(fù)責(zé)對(duì)已占用物理存儲(chǔ)器的回收。堆管理也是Windows提供的一種內(nèi)存管理機(jī)制。主要用來(lái)分配小的數(shù)據(jù)塊。與Windows的其他兩種內(nèi)存管理機(jī)制虛擬內(nèi)存和內(nèi)存映射文件相比,堆可以不必考慮諸如系統(tǒng)的分配粒度和頁(yè)面邊界之類比較煩瑣而又容易忽視的問(wèn)題,可將注意力集中于對(duì)程序功能代碼的設(shè)計(jì)上。但是使用堆去分配、釋放內(nèi)存的速度要比其他兩種機(jī)制慢的多,而且不具備直接控制物理存儲(chǔ)器提交與回收的能力。

              在進(jìn)程剛啟動(dòng)時(shí),系統(tǒng)便在剛創(chuàng)建的進(jìn)程虛擬地址空間中創(chuàng)建了一個(gè)堆,該堆即為進(jìn)程的默認(rèn)堆,缺省大小為1MB,該值允許在鏈接程序時(shí)被更改。進(jìn)程的默認(rèn)堆是比較重要的,可供眾多Windows函數(shù)使用。在使用時(shí),系統(tǒng)必須保證在規(guī)定的時(shí)間內(nèi),每此只有一個(gè)線程能夠分配和釋放默認(rèn)堆中的內(nèi)存塊。雖然這種限制將會(huì)對(duì)訪問(wèn)速度產(chǎn)生一定的影響,但卻可以保證進(jìn)程中的多個(gè)線程在同時(shí)調(diào)用各種Windows函數(shù)時(shí)對(duì)默認(rèn)堆的順序訪問(wèn)。在進(jìn)程中允許使用多個(gè)堆,進(jìn)程中包括默認(rèn)堆在內(nèi)的每個(gè)堆都有一個(gè)堆句柄來(lái)標(biāo)識(shí)。與自己創(chuàng)建的堆不同,進(jìn)程默認(rèn)堆的創(chuàng)建、銷毀均由系統(tǒng)來(lái)完成,而且其生命期早在進(jìn)程開(kāi)始執(zhí)行之前就已經(jīng)開(kāi)始,雖然在程序中可以通過(guò)GetProcessHeap()函數(shù)得到進(jìn)程的默認(rèn)堆句柄,但卻不允許調(diào)用HeapDestroy()函數(shù)顯式將其撤消。

              2 對(duì)動(dòng)態(tài)創(chuàng)建堆的需求

              前面曾提到,在進(jìn)程中除了進(jìn)程默認(rèn)堆外,還可以在進(jìn)程虛擬地址空間中動(dòng)態(tài)創(chuàng)建一些獨(dú)立的堆。至于在程序設(shè)計(jì)時(shí)究竟需不需要?jiǎng)討B(tài)創(chuàng)建獨(dú)立的堆可以從是否有保護(hù)組件的需要、是否能更加有效地對(duì)內(nèi)存進(jìn)行管理、是否有進(jìn)行本地訪問(wèn)的需要、是否有減少線程同步開(kāi)銷的需要以及是否有迅速釋放堆的需要等幾個(gè)方面去考慮。

              對(duì)于是否有保護(hù)組件的需要這一原則比較容易理解。在圖1中,左邊的圖表示了一個(gè)鏈表(節(jié)點(diǎn)結(jié)構(gòu))組件和一個(gè)樹(shù)(分支結(jié)構(gòu))組件共同使用一個(gè)堆的情況。在這種情況下,由于兩組件數(shù)據(jù)在堆中的混合存放,如果節(jié)點(diǎn)3(屬于鏈表組件)的后幾個(gè)字節(jié)由于被錯(cuò)誤改寫,將有可能影響到位于其后的分支2(屬于樹(shù)組件)。這將致使樹(shù)組件的相關(guān)代碼在遍歷其樹(shù)時(shí)由于內(nèi)存被破壞而無(wú)法進(jìn)行。究其原因,樹(shù)組件的內(nèi)存是由于鏈表組建對(duì)其自身的錯(cuò)誤操作而引起的。如果采用右圖所示方式,將樹(shù)組件和鏈表組件分別存放于一個(gè)獨(dú)立的堆中,上述情況顯然不會(huì)發(fā)生,錯(cuò)誤將被局限于進(jìn)行了錯(cuò)誤操作的鏈表組件,而樹(shù)組件由于存放在獨(dú)立的堆中而受到了保護(hù)。


            圖1 動(dòng)態(tài)創(chuàng)建堆在保護(hù)組件中的作用

              在上圖中,如果鏈表組件的每個(gè)節(jié)點(diǎn)占用12個(gè)字節(jié),每個(gè)樹(shù)組件的分支占用16個(gè)字節(jié)如果這些長(zhǎng)度不一的對(duì)象共用一個(gè)堆(左圖),在左圖中這些已經(jīng)分配了內(nèi)存的對(duì)象已占滿了堆,如果其中有節(jié)點(diǎn)2和節(jié)點(diǎn)4釋放,將會(huì)產(chǎn)生24個(gè)字節(jié)的碎片,如果試圖在24個(gè)字節(jié)的空閑區(qū)間內(nèi)分配一個(gè)16字節(jié)的分支對(duì)象,盡管要分配的字節(jié)數(shù)小于空閑字節(jié)數(shù),但分配仍將失敗。只有在堆棧中分配大小相同的對(duì)象才可以實(shí)行更加有效的內(nèi)存管理。如果將樹(shù)組件換成其他長(zhǎng)度為12字節(jié)的組件,那么在釋放一個(gè)對(duì)象后,另一個(gè)對(duì)象就可以恰好填充到此剛釋放的對(duì)象空間中。

              進(jìn)行本地訪問(wèn)的需要也是一條比較重要的原則。系統(tǒng)會(huì)經(jīng)常在內(nèi)存與系統(tǒng)頁(yè)文件之間進(jìn)行頁(yè)面交換,但如果交換次數(shù)過(guò)多,系統(tǒng)的運(yùn)行性能就將受很大的影響。因此在程序設(shè)計(jì)時(shí)應(yīng)盡量避免系統(tǒng)頻繁交換頁(yè)面,如果將那些會(huì)被同時(shí)訪問(wèn)到的數(shù)據(jù)分配在相互靠近的位置上,將會(huì)減少系統(tǒng)在內(nèi)存和頁(yè)文件之間的頁(yè)面交換頻率。

              線程同步開(kāi)銷指的是默認(rèn)條件下以順序方式運(yùn)行的堆為保護(hù)數(shù)據(jù)在多個(gè)線程試圖同時(shí)訪問(wèn)時(shí)不受破壞而必須執(zhí)行額外代碼所花費(fèi)的開(kāi)銷。這種開(kāi)銷保證了堆對(duì)線程的安全性,因此是有必要的,但對(duì)于大量的堆分配操作,這種額外的開(kāi)銷將成為一個(gè)負(fù)擔(dān),并降低程序的運(yùn)行性能。為避免這種額外的開(kāi)銷,可以在創(chuàng)建新堆時(shí)通知系統(tǒng)只有單個(gè)線程對(duì)訪問(wèn)。此時(shí)堆對(duì)線程的安全性將有應(yīng)用程序來(lái)負(fù)責(zé)。

              最后如果有迅速釋放堆的需要,可將專用堆用于某些數(shù)據(jù)結(jié)構(gòu),并以整個(gè)堆去釋放,而不再顯式地釋放在堆中分配的每一個(gè)內(nèi)存塊。對(duì)于大多數(shù)應(yīng)用程序,這樣的處理將能以更快的速度運(yùn)行。



              3 創(chuàng)建堆

              在進(jìn)程中,如果需要可以在原有默認(rèn)堆的基礎(chǔ)上動(dòng)態(tài)創(chuàng)建一個(gè)堆,可由HeapCreate()函數(shù)完成:

            HANDLE HeapCreate(
             DWORD flOptions,
             DWORD dwInitialSize,
             DWORD dwMaximumSize
            );

              其第一個(gè)參數(shù)flOptions指定了對(duì)新建堆的操作屬性。該標(biāo)志將會(huì)影響一些堆函數(shù)如HeapAlloc()、HeapFree()、HeapReAlloc()和HeapSize()等對(duì)新建堆的訪問(wèn)。其可能的取值為下列標(biāo)志及其組合:

            屬性標(biāo)志 說(shuō)明
            HEAP_GENERATE_EXCEPTIONS 在遇到由于內(nèi)存越界等而引起的函數(shù)失敗時(shí),由系統(tǒng)拋出一個(gè)異常來(lái)指出此失敗,而不是簡(jiǎn)單的返回NULL指針。
            HEAP_NO_SERIALIZE 指明互斥現(xiàn)象不會(huì)出現(xiàn)

              參數(shù)dwInitialSize和dwMaximumSize分別為堆的初始大小和堆棧的最大尺寸。其中,dwInitialSize的值決定了最初提交給堆的字節(jié)數(shù)。如果設(shè)置的數(shù)值不是頁(yè)面大小的整數(shù)倍,則將被圓整到鄰近的頁(yè)邊界處。而dwMaximumSize則實(shí)際上是系統(tǒng)能為堆保留的地址空間區(qū)域的最大字節(jié)數(shù)。如果該值為0,那么將創(chuàng)建一個(gè)可擴(kuò)展的堆,堆的大小僅受可用內(nèi)存的限制。如果應(yīng)用程序需要分配大的內(nèi)存塊,通常要將該參數(shù)設(shè)置為0。如果dwMaximumSize大于0,則該值限定了堆所能創(chuàng)建的最大值,HeapCreate()同樣也要將該值圓整到鄰近的頁(yè)邊界,然后再在進(jìn)程的虛擬地址空間為堆保留該大小的一塊區(qū)域。在這種堆中分配的內(nèi)存塊大小不能超過(guò)0x7FFF8字節(jié),任何試圖分配更大內(nèi)存塊的行為將會(huì)失敗,即使是設(shè)置的堆大小足以容納該內(nèi)存塊。如果HeapCreate()成功執(zhí)行,將會(huì)返回一個(gè)標(biāo)識(shí)新堆的句柄,并可供其他堆函數(shù)使用。

              需要特別說(shuō)明的是,在設(shè)置第一個(gè)參數(shù)時(shí),對(duì)HEAP_NO_SERIALIZE的標(biāo)志的使用要謹(jǐn)慎,一般應(yīng)避免使用該標(biāo)志。這是同后續(xù)將要進(jìn)行的堆函數(shù)HeapAlloc()的執(zhí)行過(guò)程有關(guān)系的,在HeapAlloc()試圖從堆中分配一個(gè)內(nèi)存塊時(shí),將執(zhí)行下述幾步操作:

              1) 遍歷分配的和釋放的內(nèi)存塊的鏈接表

              2) 搜尋一個(gè)空閑內(nèi)存塊的地址

              3) 通過(guò)將空閑內(nèi)存塊標(biāo)記為"已分配"來(lái)分配新內(nèi)存塊

              4) 將新分配的內(nèi)存塊添加到內(nèi)存塊列表

              如果這時(shí)有兩個(gè)線程1、2試圖同時(shí)從一個(gè)堆中分配內(nèi)存塊,那么線程1在執(zhí)行了上面的1和2步后將得到空間內(nèi)存塊的地址。但是由于CPU對(duì)線程運(yùn)行時(shí)間的分片,使得線程1在執(zhí)行第3步操作前有可能被線程2搶走執(zhí)行權(quán)并有機(jī)會(huì)去執(zhí)行同樣的1、2步操作,而且由于先執(zhí)行的線程1并沒(méi)有執(zhí)行到第3步,因此線程2會(huì)搜尋到同一個(gè)空閑內(nèi)存塊的地址,并將其標(biāo)記為已分配。而線程1在恢復(fù)運(yùn)行后并不能知曉該內(nèi)存塊已被線程2標(biāo)記過(guò),因此會(huì)出現(xiàn)兩個(gè)線程軍認(rèn)為其分配的是空閑的內(nèi)存塊,并更新各自的聯(lián)接表。顯然,象這種兩個(gè)線程擁有完全相同內(nèi)存塊地址的錯(cuò)誤是非常嚴(yán)重而又是難以發(fā)現(xiàn)的。

              由于只有在多個(gè)線程同時(shí)進(jìn)行操作時(shí)才有可能出現(xiàn)上述問(wèn)題,一種簡(jiǎn)單的解決的辦法就是不使用HEAP_NO_SERIALIZE標(biāo)志而只允許單個(gè)線程獨(dú)占地對(duì)堆及其聯(lián)接表?yè)碛性L問(wèn)權(quán)。如果一定要使用此標(biāo)志,為了安全起見(jiàn),必須確保進(jìn)程為單線程的或是在進(jìn)程中使用了多線程,但只有單個(gè)線程對(duì)堆進(jìn)行訪問(wèn)。再就是使用了多線程,也有多個(gè)線程對(duì)堆進(jìn)行了訪問(wèn),但這些線程通過(guò)使用某種線程同步手段。如果可以確保以上幾條中的一條成立,也是可以安全使用HEAP_NO_SERIALIZE標(biāo)志的,而且還將擁有快的訪問(wèn)速度。如果不能肯定上述條件是否滿足,建議不使用此標(biāo)志而以順序的方式訪問(wèn)堆,雖然線程速度會(huì)因此而下降但卻可以確保堆及其中數(shù)據(jù)的不被破壞。

              4 從堆中分配內(nèi)存塊

              在成功創(chuàng)建一個(gè)堆后,可以調(diào)用HeapAlloc()函數(shù)從堆中分配內(nèi)存塊。在此,除了可以從用HeapCreate()創(chuàng)建的動(dòng)態(tài)堆中分配內(nèi)存塊,也可以直接從進(jìn)程的默認(rèn)堆中分配內(nèi)存塊。下面先給出HeapCreate()的函數(shù)原型:

            LPVOID HeapAlloc(
             HANDLE hHeap,
             DWORD dwFlags,
             DWORD dwBytes
            );

              其中,參數(shù)hHeap為要分配的內(nèi)存塊來(lái)自的堆的句柄,可以是從HeapCreate()創(chuàng)建的動(dòng)態(tài)堆句柄也可以是由GetProcessHeap()得到的默認(rèn)堆句柄。參數(shù)dwFlags指定了影響堆分配的各個(gè)標(biāo)志。該標(biāo)志將覆蓋在調(diào)用HeapCreate()時(shí)所指定的相應(yīng)標(biāo)志,可能的取值為:

            標(biāo)志 說(shuō)明
            HEAP_GENERATE_EXCEPTIONS 該標(biāo)志指定在進(jìn)行諸如內(nèi)存越界操作等情況時(shí)將拋出一個(gè)異常而不是簡(jiǎn)單的返回NULL指針
            HEAP_NO_SERIALIZE 強(qiáng)制對(duì)HeapAlloc()的調(diào)用將與訪問(wèn)同一個(gè)堆的其他線程不按照順序進(jìn)行
            HEAP_ZERO_MEMORY 如果使用了該標(biāo)志,新分配內(nèi)存的內(nèi)容將被初始化為0

              最后一個(gè)參數(shù)dwBytes設(shè)定了要從堆中分配的內(nèi)存塊的大小。如果HeapAlloc()執(zhí)行成功,將會(huì)返回從堆中分配的內(nèi)存塊的地址。如果由于內(nèi)存不足或是其他一些原因而引起HeapAlloc()函數(shù)的執(zhí)行失敗,將會(huì)引發(fā)異常。通過(guò)異常標(biāo)志可以得到引起內(nèi)存分配失敗的原因:如果為STATUS_NO_MEMORY則表明是由于內(nèi)存不足引起的;如果是STATUS_ACCESS_VIOLATION則表示是由于堆被破壞或函數(shù)參數(shù)不正確而引起分配內(nèi)存塊的嘗試失敗。以上異常只有在指定了HEAP_GENERATE_EXCEPTIONS標(biāo)志時(shí)才會(huì)發(fā)生,如果沒(méi)有指定此標(biāo)志,在出現(xiàn)類似錯(cuò)誤時(shí)HeapAlloc()函數(shù)只是簡(jiǎn)單的返回NULL指針。

              在設(shè)置dwFlags參數(shù)時(shí),如果先前用HeapCreate()創(chuàng)建堆時(shí)曾指定過(guò)HEAP_GENERATE_EXCEPTIONS標(biāo)志,就不必再去設(shè)置HEAP_GENERATE_EXCEPTIONS標(biāo)志了,因?yàn)镠EAP_GENERATE_EXCEPTIONS標(biāo)志已經(jīng)通知堆在不能分配內(nèi)存塊時(shí)將會(huì)引發(fā)異常。另外,對(duì)HEAP_NO_SERIALIZE標(biāo)志的設(shè)置應(yīng)慎重,與在HeapCreate()函數(shù)中使用HEAP_NO_SERIALIZE標(biāo)志類似,如果在同一時(shí)間有其他線程使用同一個(gè)堆,那么該堆將會(huì)被破壞。如果是在進(jìn)程默認(rèn)堆中進(jìn)行內(nèi)存塊的分配則要絕對(duì)禁用此標(biāo)志。

              在使用堆函數(shù)HeapAlloc()時(shí)要注意:堆在內(nèi)存管理中的使用主要是用來(lái)分配一些較小的數(shù)據(jù)塊,如果要分配的內(nèi)存塊在1MB左右,那么就不要再使用堆來(lái)管理內(nèi)存了,而應(yīng)選擇虛擬內(nèi)存的內(nèi)存管理機(jī)制。


              5 再分配內(nèi)存塊

              在程序設(shè)計(jì)時(shí)經(jīng)常會(huì)由于開(kāi)始時(shí)預(yù)見(jiàn)不足而造成在堆中分配的內(nèi)存塊大小的不合適(多數(shù)情況是開(kāi)始時(shí)分配的內(nèi)存較小,而后來(lái)實(shí)際需要更多的數(shù)據(jù)復(fù)制到內(nèi)存塊中去)這就需要在分配了內(nèi)存塊后再根據(jù)需要調(diào)整其大小。堆函數(shù)HeapReAlloc()將完成這一功能,其函數(shù)原型為:

            LPVOID HeapReAlloc(
             HANDLE hHeap,
             DWORD dwFlags,
             LPVOID lpMem,
             DWORD dwBytes
            );

              其中,參數(shù)hHeap為包含要調(diào)整其大小的內(nèi)存塊的堆的句柄。dwFlags參數(shù)指定了在更改內(nèi)存塊大小時(shí)HeapReAlloc()函數(shù)所使用的標(biāo)志。其可能的取值為HEAP_GENERATE_EXCEPTIONS、HEAP_NO_SERIALIZE、HEAP_REALLOC_IN_PLACE_ONLY和HEAP_ZERO_MEMORY,其中前兩個(gè)標(biāo)志的作用與在HeapAlloc()中的作用相同。HEAP_REALLOC_IN_PLACE_ONLY標(biāo)志在內(nèi)存塊被加大時(shí)不移動(dòng)堆中的內(nèi)存塊,在沒(méi)有設(shè)置此標(biāo)志的情況下如果對(duì)內(nèi)存進(jìn)行增大,那么HeapReAlloc()函數(shù)將有可能將原內(nèi)存塊移動(dòng)到一個(gè)新的地址。顯然,在設(shè)置了該標(biāo)志禁止內(nèi)存快首地址進(jìn)行調(diào)整時(shí),將有可能出現(xiàn)沒(méi)有足夠的內(nèi)存供試圖增大的內(nèi)存塊使用,對(duì)于這種情況,函數(shù)對(duì)內(nèi)存塊增大調(diào)整的操作是失敗的,內(nèi)存塊將仍保留原有的大小和位置。HEAP_ZERO_MEMORY標(biāo)志的用處則略有不同,如果內(nèi)存快經(jīng)過(guò)調(diào)整比以前大,那么新增加的那部分內(nèi)存將被初始化為0;如果經(jīng)過(guò)調(diào)整內(nèi)存塊縮小了,那么該標(biāo)志將不起任何作用。

              函數(shù)的最后兩個(gè)參數(shù)lpMem和dwBytes分別為指向再分配內(nèi)存塊的指針和再分配的字節(jié)數(shù)。如果函數(shù)成功執(zhí)行,將返回新的改變了大小的內(nèi)存塊的地址。如果在調(diào)用時(shí)使用了HEAP_REALLOC_IN_PLACE_ONLY標(biāo)志,那么返回的地址將與原內(nèi)存塊地址相同。如果因?yàn)閮?nèi)存不足等原因而引起函數(shù)的執(zhí)行失敗,函數(shù)將返回一個(gè)NULL指針。但是HeapReAlloc()的執(zhí)行失敗并不會(huì)影響原內(nèi)存塊,它將保持原來(lái)的大小和位置繼續(xù)存在。可以通過(guò)HeapSize()函數(shù)來(lái)檢索內(nèi)存塊的實(shí)際大小。

              6 釋放堆內(nèi)存、撤消堆

              在不再需要使用堆中的內(nèi)存塊時(shí),可以通過(guò)HeapFree()將其予以釋放。該函數(shù)結(jié)構(gòu)比較簡(jiǎn)單,只含有三個(gè)參數(shù):

            BOOL HeapFree(
             HANDLE hHeap,
             DWORD dwFlags,
             LPVOID lpMem
            );

              其中,hHeap為要包含要釋放內(nèi)存塊的堆的句柄;參數(shù)dwFlags為堆棧的釋放選項(xiàng)可以是0,也可以是HEAP_NO_SERIALIZE;最后的參數(shù)lpMem為指向內(nèi)存塊的指針。如果函數(shù)成功執(zhí)行,將釋放指定的內(nèi)存塊,并返回TRUE。該函數(shù)的主要作用是可以用來(lái)幫助堆管理器回收某些不使用的物理存儲(chǔ)器以騰出更多的空閑空間,但是并不能保證一定會(huì)成功。

              最后,在程序退出前或是應(yīng)用程序不再需要其創(chuàng)建的堆了,可以調(diào)用HeapDestory()函數(shù)將其銷毀。該函數(shù)只包含一個(gè)參數(shù)--待銷毀的堆的句柄。HeapDestory()的成功執(zhí)行將可以釋放堆中包含的所有內(nèi)存塊,也可將堆占用的物理存儲(chǔ)器和保留的地址空間區(qū)域全部重新返回給系統(tǒng)并返回TRUE。該函數(shù)只對(duì)由HeapCreate()顯式創(chuàng)建的堆起作用,而不能銷毀進(jìn)程的默認(rèn)堆,如果強(qiáng)行將由GetProcessHeap()得到的默認(rèn)堆的句柄作為參數(shù)去調(diào)用HeapDestory(),系統(tǒng)將會(huì)忽略對(duì)該函數(shù)的調(diào)用。


              7 對(duì)new與delete操作符的重載

              new與delete內(nèi)存空間動(dòng)態(tài)分配操作符是C++中使用堆進(jìn)行內(nèi)存管理的一種常用方式,在程序運(yùn)行過(guò)程中可以根據(jù)需要隨時(shí)通過(guò)這兩個(gè)操作符建立或刪除堆對(duì)象。new操作符將在堆中分配一個(gè)足夠大小的內(nèi)存塊以存放指定類型的對(duì)象,如果每次構(gòu)造的對(duì)象類型不同,則需要按最大對(duì)象所占用的空間來(lái)進(jìn)行分配。new操作符在成功執(zhí)行后將返回一個(gè)類型與new所分配對(duì)象相匹配的指針,如果不匹配則要對(duì)其進(jìn)行強(qiáng)制類型轉(zhuǎn)換,否則將會(huì)編譯出錯(cuò)。在不再需要這個(gè)對(duì)象的時(shí)候,必須顯式調(diào)用delete操作符來(lái)釋放此空間。這一點(diǎn)是非常重要的,如果在預(yù)分配的緩沖里構(gòu)造另一個(gè)對(duì)象之前或者在釋放緩沖之前沒(méi)有顯式調(diào)用delete操作符,那么程序將產(chǎn)生不可預(yù)料的后果。在使用delete操作符時(shí),應(yīng)注意以下幾點(diǎn):

              1) 它必須使用于由運(yùn)算符new返回的指針

              2) 該操作符也適用于NULL指針

              3) 指針名前只用一對(duì)方括號(hào)符,并且不管所刪除數(shù)組的維數(shù),忽略方括號(hào)內(nèi)的任何數(shù)字

            class CVMShow{
             private:
              static HANDLE m_sHeap;
              static int m_sAllocedInHeap;
             public:
              LPVOID operator new(size_t size);
              void operator delete(LPVOID pVoid);
            }

            ……
            HANDLE m_sHeap = NULL;
            int m_sAllocedInHeap = 0;
            LPVOID CVMShow::operator new(size_t size)
            {
             if (m_sHeap == NULL)
              m_sHeap = HeapCreate(HEAP_GENERATE_EXCEPTIONS, 0, 0);
              LPVOID pVoid = HeapAlloc(m_sHeap, 0, size);
              if (pVoid == NULL)
               return NULL;
               m_sAllocedInHeap++;
              return pVoid;
            }
            void CVMShow::operator delete(LPVOID pVoid)
            {
             if (HeapFree(m_sHeap, 0, pVoid))
              m_sAllocedInHeap--;
              if (m_sAllocedInHeap == 0)
              {
               if (HeapDestory(m_sHeap))
                m_sHeap = NULL;
              }
            }

              在程序中除了直接用上述方法使用new和delete來(lái)建立和刪除堆對(duì)象外,還可以通過(guò)為C++類重載new和delete操作符來(lái)方便地利用堆棧函數(shù)。上面的代碼對(duì)它們進(jìn)行了簡(jiǎn)單的重載,并通過(guò)靜態(tài)變量m_sHeap和m_sAllocedInHeap在類CVMShow的所有實(shí)例間共享唯一的堆句柄(因?yàn)樵谶@里CVMShow類的所有實(shí)例都是在同一個(gè)堆中進(jìn)行內(nèi)存分配的)和已分配類對(duì)象的計(jì)數(shù)。這兩個(gè)靜態(tài)變量在代碼開(kāi)始執(zhí)行時(shí)被分別初始化為NULL指針和0計(jì)數(shù)。

              重載的new操作符在第一次被調(diào)用時(shí),由于靜態(tài)變量m_sHeap為NULL標(biāo)志著堆尚未創(chuàng)建,就通過(guò)HeapCreate()函數(shù)創(chuàng)建一個(gè)堆并返回堆句柄到m_sHeap。隨后根據(jù)入口參數(shù)size所指定的大小在堆中分配內(nèi)存,同時(shí)已分配內(nèi)存塊計(jì)數(shù)器m_sAllocedInHeap累加。在該操作符的以后調(diào)用過(guò)程中,由于堆已經(jīng)創(chuàng)建,故不再創(chuàng)建堆,而是直接在堆中分配指定大小的內(nèi)存塊并對(duì)已分配的內(nèi)存塊個(gè)數(shù)進(jìn)行計(jì)數(shù)。

              在CVMShow類對(duì)象不再被應(yīng)用程序所使用時(shí),需要將其撤消,由重載的delete操作符完成此工作。delete操作符只接受一個(gè)LPVOID型參數(shù),即被刪除對(duì)象的地址。該函數(shù)在執(zhí)行時(shí)首先調(diào)用HeapFree()函數(shù)將指定的已分配內(nèi)存的對(duì)象釋放并對(duì)已分配內(nèi)存計(jì)數(shù)遞減1。如果該計(jì)數(shù)不為零則表明當(dāng)前堆中的內(nèi)存塊沒(méi)有全部釋放,堆暫時(shí)不予撤消。如果m_sAllocedInHeap計(jì)數(shù)減到0,則堆中已釋放完所有的CVMShow對(duì)象,可以調(diào)用HeapDestory()函數(shù)將堆銷毀,并將堆句柄m_sHeap設(shè)置為NULL指針。這里在撤消堆后將堆句柄設(shè)置為NULL指針的操作是完全必要的。如果不執(zhí)行該操作,當(dāng)程序再次調(diào)用new操作符去分配一個(gè)CVMShow類對(duì)象時(shí)將會(huì)認(rèn)為堆是存在的而會(huì)試圖在已撤消的堆中去分配內(nèi)存,顯然將會(huì)導(dǎo)致失敗。

              象CVMShow這樣設(shè)計(jì)的類通過(guò)對(duì)new和delete操作符的重載,并且在一個(gè)堆中為所有的CVMShow類對(duì)象進(jìn)行分配,可以節(jié)省在為每一個(gè)類都創(chuàng)建堆的分配開(kāi)銷與內(nèi)存。這樣的處理還可以讓每一個(gè)類都擁有屬于自己的堆,并且允許派生類對(duì)其共享,這在程序設(shè)計(jì)中也是比較好的一種處理方法。

              8 小結(jié)

              在使用堆時(shí)有時(shí)會(huì)造成系統(tǒng)運(yùn)行速度的減慢,通常是由以下原因造成的:分配操作造成的速度減慢;釋放操作造成的速度減慢;堆競(jìng)爭(zhēng)造成的速度減慢;堆破壞造成的速度減慢;頻繁的分配和重分配造成的速度減慢等。其中,競(jìng)爭(zhēng)是在分配和釋放操作中導(dǎo)致速度減慢的問(wèn)題。基于上述原因,建議不要在程序中過(guò)于頻繁的使用堆。文中所述代碼均在Windows 2000 Professional下由Microsoft Visual C++ 6.0編譯通過(guò)。
            Posted on 2005-11-11 13:45 艾凡赫 閱讀(388) 評(píng)論(0)  編輯 收藏 引用 所屬分類: MFC技術(shù)
            伊人久久大香线焦AV综合影院| 久久久久国产| 亚洲精品tv久久久久| 国产精品岛国久久久久| 精品久久久久久久久午夜福利| 久久人人爽人人爽人人爽| 久久99热这里只有精品国产| 国产一级持黄大片99久久| 久久久无码精品亚洲日韩按摩 | 久久国产精品久久国产精品| 伊人久久大香线蕉亚洲五月天| 狠狠色综合网站久久久久久久高清| 久久亚洲AV无码精品色午夜麻豆| 性做久久久久久久久| 亚洲精品乱码久久久久久蜜桃| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 成人综合伊人五月婷久久| 色综合久久久久综合体桃花网| 无码人妻久久一区二区三区免费| 亚洲AV无码久久精品蜜桃| 久久亚洲精精品中文字幕| 无码国内精品久久人妻蜜桃 | 一本色综合网久久| 久久久噜噜噜久久中文福利| 粉嫩小泬无遮挡久久久久久| 久久免费的精品国产V∧| 久久精品国产精品青草app| 久久久久久免费一区二区三区 | 久久久国产打桩机| 久久久久亚洲AV无码麻豆| 久久精品无码一区二区无码| 狠狠色婷婷综合天天久久丁香| 色综合久久精品中文字幕首页| www.久久热.com| 99久久精品国产一区二区| 精品久久人人爽天天玩人人妻| 中文字幕精品无码久久久久久3D日动漫| 亚洲欧洲久久av| 精品一区二区久久| 久久涩综合| 国产国产成人精品久久|