保持C/C++程序代碼可伸縮性

字號(hào):

在今天,已有許多的32位應(yīng)用程序感到,在32位平臺(tái)上可用的虛擬內(nèi)存受到了一定的限制,對(duì)程序開發(fā)者來說,即使是開始關(guān)注64位平臺(tái)時(shí),也不得不維護(hù)軟件的32位版本,這就需要一種方法,以使代碼的兩個(gè)版本都保持相當(dāng)?shù)目缮炜s性。
    目前的內(nèi)存剖析工具能幫助確定,當(dāng)程序達(dá)到峰值內(nèi)存使用量時(shí),都發(fā)生了什么,但是這些工具都過于關(guān)注已分配的內(nèi)存塊,而不是已提交的虛擬內(nèi)存地址空間,而這兩種衡量標(biāo)準(zhǔn)沒有直接的相關(guān)性,如內(nèi)存泄漏、內(nèi)存碎片、內(nèi)存塊內(nèi)的空間浪費(fèi)、或過度延遲的內(nèi)存單元重新分配這些因素,都會(huì)導(dǎo)致不必要的虛擬內(nèi)存提交。運(yùn)行時(shí)分析工具如IBM Rational Purify或Parasoft Inuse均可以提供內(nèi)存泄漏及已用內(nèi)存的描述,這些信息是非常有用的,但是,一個(gè)特殊的內(nèi)存塊也許可能、也許不可能影響到虛擬內(nèi)存覆蓋區(qū),另外,甚至一個(gè)有碎片的內(nèi)存堆中的一個(gè)小塊,也能直接影響到虛擬內(nèi)存覆蓋區(qū)。從另一方面來說,在此范圍內(nèi)的任意內(nèi)存塊--甚至泄漏的塊,對(duì)虛擬內(nèi)存覆蓋區(qū)來說,也不會(huì)與之有什么關(guān)系,除非每一個(gè)此范圍內(nèi)有用的內(nèi)存塊能重新分配到一個(gè)更緊湊的范圍內(nèi),這就有點(diǎn)像Java或托管程序的垃圾回收機(jī)制,但對(duì)大多數(shù)C/C++本地應(yīng)用程序來說,就絕對(duì)不可能了,因?yàn)樵谔摂M內(nèi)存空間中,它們內(nèi)存塊的位置是不確定的。
    至于本地代碼,不必要的虛擬內(nèi)存使用,這個(gè)實(shí)際的問題,比未清理的內(nèi)存塊這個(gè)理論上的問題,更加有實(shí)質(zhì)性。未清理的內(nèi)存塊可能導(dǎo)致虛擬內(nèi)存的浪費(fèi),造成過多的系統(tǒng)開銷,但或者不會(huì);這完全依賴于堆管理器是否提交了更多的虛擬內(nèi)存,以支撐這種浪費(fèi)。某些很小的未使用的內(nèi)存塊,不會(huì)引起不必要的堆"擴(kuò)展"。與其讓你來猜哪一個(gè)或多少已浪費(fèi)的內(nèi)存塊導(dǎo)致了堆擴(kuò)展,倒不如學(xué)會(huì)怎樣判定出有意義的浪費(fèi)是什么。當(dāng)堆中包含不再使用的內(nèi)存塊時(shí),此時(shí)通過加入對(duì)未縮減堆的檢查,就能確定出與你的程序虛擬內(nèi)存要求有很大關(guān)系的、必須進(jìn)行的內(nèi)存塊清理。
    為找出哪一個(gè)堆中的內(nèi)存塊需多留意,必須在程序中加入一些額外的代碼,以跟蹤內(nèi)存堆范圍及已分配的內(nèi)存塊。對(duì)額外的代碼進(jìn)行條件編譯,生成一個(gè)特定的版本,也許是一個(gè)不錯(cuò)的辦法。
    為達(dá)到此目的,需編寫自定義的內(nèi)存分配例程,并跟蹤每一個(gè)內(nèi)存塊,另有一個(gè)自定義的釋放例程,且跟蹤虛擬內(nèi)存中堆的位置,請(qǐng)參見例1與例2的偽代碼算法??赡苓€需編寫自定義的訪問函數(shù)以標(biāo)記出訪問過的內(nèi)存塊,以便于在適當(dāng)?shù)臅r(shí)候釋放虛擬內(nèi)存,所有這些并不需要過多的內(nèi)存開銷。另一方面,如果你的程序以堆的形式使用了大量的內(nèi)存,那么將會(huì)極大地降低性能,此處的方法也不是長久之計(jì)。
    例1:
    /* 輸入?yún)?shù)*/
    ADDRESS triggerAddr
    SIZE triggerSize
    LIST a list of tracked heap ranges
    IF (the virtual memory at triggerAddr is tracked on the list as part of a heap range)
    DO
    IF (triggerAddr + triggerSize >
    (the tracked upper boundary of that heap range))
    DO
    /* 一個(gè)現(xiàn)有的堆范圍被擴(kuò)展 */
    make system call(s) to determine the base and extent of the newly committed range that contains the addresses from triggerAddr to (triggerAddr + triggerSize)
    update the size of the tracked heap range to indicate its new upper limit
    END
    END
    ELSE DO
    /* 在triggerAddr中有一個(gè)新的堆范圍 */
    make system call(s) to determine the base and extent of the newly committed range that contains the addresses from triggerAddr to (triggerAddr + triggerSize)
    track the new committed range in the list of heap ranges
    END