| 導購 | 订阅 | 在线投稿
分享
 
 
當前位置: 王朝網路 >> c/c++ >> 保持C/C++程序代碼的可伸縮性
 

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

2008-06-01 02:06:04  編輯來源:互聯網  简体版  手機版  評論  字體: ||
 
 
  在今天,已有許多的32位應用程序感到,在32位平台上可用的虛擬內存受到了一定的限制,對程序開發者來說,即使是開始關注64位平台時,也不得不維護軟件的32位版本,這就需要一種方法,以使代碼的兩個版本都保持相當的可伸縮性。

  目前的內存剖析工具能幫助確定,當程序達到峰值內存使用量時,都發生了什麽,但是這些工具都過于關注已分配的內存塊,而不是已提交的虛擬內存地址空間,而這兩種衡量標准沒有直接的相關性,如內存泄漏、內存碎片、內存塊內的空間浪費、或過度延遲的內存單元重新分配這些因素,都會導致不必要的虛擬內存提交。運行時分析工具如IBM Rational Purify或Parasoft Inuse均可以提供內存泄漏及已用內存的描述,這些信息是非常有用的,但是,一個非凡的內存塊也許可能、也許不可能影響到虛擬內存覆蓋區,另外,甚至一個有碎片的內存堆中的一個小塊,也能直接影響到虛擬內存覆蓋區。從另一方面來說,在此範圍內的任意內存塊--甚至泄漏的塊,對虛擬內存覆蓋區來說,也不會與之有什麽關系,除非每一個此範圍內有用的內存塊能重新分配到一個更緊湊的範圍內,這就有點像Java或托管程序的垃圾回收機制,但對大多數C/C++本地應用程序來說,就絕對不可能了,因爲在虛擬內存空間中,它們內存塊的位置是不確定的。

  至于本地代碼,不必要的虛擬內存使用,這個實際的問題,比未清理的內存塊這個理論上的問題,更加有實質性。未清理的內存塊可能導致虛擬內存的浪費,造成過多的系統開銷,但或者不會;這完全依靠于堆治理器是否提交了更多的虛擬內存,以支撐這種浪費。某些很小的未使用的內存塊,不會引起不必要的堆"擴展"。與其讓你來猜哪一個或多少已浪費的內存塊導致了堆擴展,倒不如學會怎樣判定出有意義的浪費是什麽。當堆中包含不再使用的內存塊時,此時通過加入對未縮減堆的檢查,就能確定出與你的程序虛擬內存要求有很大關系的、必須進行的內存塊清理。

  爲找出哪一個堆中的內存塊需多留意,必須在程序中加入一些額外的代碼,以跟蹤內存堆範圍及已分配的內存塊。對額外的代碼進行條件編譯,生成一個特定的版本,也許是一個不錯的辦法。

  爲達到此目的,需編寫自定義的內存分配例程,並跟蹤每一個內存塊,另有一個自定義的釋放例程,且跟蹤虛擬內存中堆的位置,請參見例1與例2的僞代碼算法。可能還需編寫自定義的訪問函數以標記出訪問過的內存塊,以便于在適當的時候釋放虛擬內存,所有這些並不需要過多的內存開銷。另一方面,假如你的程序以堆的形式使用了大量的內存,那麽將會極大地降低性能,此處的方法也不是長久之計。

  例1:

  

  /* 輸入參數*/

  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

  /* 一個現有的堆範圍被擴展 */

  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中有一個新的堆範圍 */

  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

  例2:

  

  /* 輸入參數 */

  ADDRESS triggerAddr

  SIZE triggerSize

  LIST a list of tracked heap ranges

  /* 局部變量 */

  ADDRESS origRangeBase

  SIZE origRangeSize

  BOOL bFoundChange

  bFoundChange = FALSE

  IF (the virtual memory at triggerAddr is not tracked on the heap range list as part of a heap range)

  DO

  /*似乎我們已經清楚此次釋放了。*/

  END

  ELSE IF (an Access exception occurs when memory at triggerAddr is read)

  DO

  bFoundChange = TRUE

  END

  IF (bFoundChange) DO

  /*因爲之前內存塊占用的空間被釋放了,所以堆占用的虛擬內存範圍就改變了。*/

  make system calls to determine the bases and extents of the tracked committed heap ranges in the immediate vicinity of the decommitted range that includes the addresses from triggerAddr to (triggerAddr + triggerSize)

  /*更新堆範圍跟蹤,以反映剩余提交的範圍 */

  IF (any portion of the tracked heap range that contained the block at TriggerAddr is still committed)

  DO

  update the heap range list to track just the portion(s) of that range that remain committed

  END

  ELSE

  DO

  delete the list element that tracks the range

  END

  END

  

  

  

  

  

   QQread.com

   推出Windows2003教程

  

  

   win2003安裝介紹

   win2003網絡優化

   win2003使用技巧

  

  

   win2003系統故障

   服務器配置

   專家答疑

  

  

  

  

  

  

   更多的請看:http://www.qqread.com/windows/2003/index.Html跟蹤堆內存塊

  可使用自定義的內存分配函數來進行內存塊的跟蹤,而這種函數最初被稱爲普通內存分配函數,舉例來說,C語言程序中一般使用malloc(),爾後,自定義的內存分配會進行以下一系列的操作:

  ·在目前已分配的內存塊列表中,跟蹤新分配的內存塊。

  ·決定是否向系統提交虛擬內存。

  ·假如虛擬內存已被提交,跟蹤包含此內存塊的堆範圍,並更新上述堆內存塊列表,以標識出從未被訪問過的內存塊。

  還需要自定義的釋放與重分配內存函數,以便通過程序中使用的內存塊的地址與大小,來更新內存塊列表。所跟蹤的堆內存塊列表應包含如下結構:

  ·內存塊的基地址。

  ·自身大小

  ·用于指示自從上次虛擬內存被提交之後,內存塊是否被訪問過的布爾值。

  當一個內存塊被釋放後,自定義的釋放代碼將會進行以下操作:

  ·假如自從上次堆擴展之後,內存塊還未被訪問過,將向程序報告。

  ·從列表中刪除跟蹤的內存塊。

  ·判定系統是否已釋放了包含此內存塊的虛擬內存。

  ·假如虛擬內存被釋放,要相應地更新,以反映剩余的堆範圍。

  當一個內存塊被重新分配時,你的自定義重新分配內存代碼必須進行以下兩種操作:首先,在釋放之後重新跟蹤內存塊,因爲重新分配的內存塊可能不在原位置;其次,在分配之後也要重新跟蹤新的基地址及分配內存塊的大小。另有一個可選的方法,你可檢查是否重新分配的內存塊被移動了,假如沒有被移動,只需僅僅更新內存塊跟蹤列表,標出此內存塊的大小;假如內存塊還在同一基地址,但是增長了,此時就要檢查堆擴展,並按照前述分配內存的方法重來一遍。

  跟蹤堆自身

  堆跟蹤取決于當內存塊被分配或釋放時,虛擬內存是否分別被提交或釋放,依此可以建立一張堆內存範圍跟蹤表,以確定在程序運行期間,虛擬內存空間中堆的確切位置,跟蹤列表中應包括如下數據:

  ·跟蹤範圍的基地址

  ·自身大小

  在Windows操作系統中,這些值可通過HeapWalk()調用獲得,此處要注重的是,HeapWalk()函數調用開銷巨大,因此,只在程序需要時調用,而不是當有內存分配或釋放時都調用。另一種Windows上的方法是使用IsBadReadPointer()函數,當一個內存塊被釋放後,你可以調用這個函數快速地判定包含此內存塊的虛擬內存是否已被釋放。另一個可以跨平台的備選方法是,可試著訪問包含此內存塊的虛擬內存,並捕捉可能發生的訪問異常。此外,只有在一種情況下會考慮使用如HeapWalk()這樣開銷巨大的函數,就是需判定鄰近剩余的已提交堆範圍。

  通過一種探測堆內存提交的算法,堆內存跟蹤列表會不斷地增長,如例1中所示。要注重的是,當你的程序分配一個內存塊時,自定義的內存分配代碼也能跟蹤到這些內存塊,並使用例1中的算法來更新包含內存塊的堆列表。假如一個內存塊的分配導致了額外的虛擬內存被提交,那麽被內存塊占用的虛擬內存會在之前就釋放,或許之前就被用作別的用途。在任一情況中,必須有條件地更新堆範圍跟蹤列表: l 假如正在跟蹤已提交範圍的基地址,此時必須更新範圍的大小,以指示出新的範圍上限。

  ·否則,必須建立一張新的堆內存範圍跟蹤表。

  假如一個新的內存塊出現在一個之前未被跟蹤的堆範圍中,就滿足了以上條件,此時明智地使用前述的系統調用可高效地跟蹤堆內存範圍。

  當你的程序釋放一個內存塊時,自定義的內存釋放代碼會使用到如例2中的算法,此算法會先判定釋放的內存是否與被跟蹤的堆內存範圍有關;接下來,必須檢查已釋放內存塊占用的空間是否仍處于提交狀態,假如是,表明了即使內存塊被釋放,虛擬內存的覆蓋區也沒有發生改變,否則,你的代碼必須進行如例2結尾處的系統調用--如Windows中的HeapWalk()--以確保跟蹤的堆是最新的,且包含堆內存塊的虛擬內存已被釋放。

  假如虛擬內存已經被提交,那麽你應該檢查那些通常包含了最近被釋放的堆內存塊的內存範圍,以確認是否有此範圍內的內存被提交,而此範圍內任何被提交的內存部分都應該是在一個堆內存範圍內,非凡是假如它包含了跟蹤列表上的內存塊,進行此檢查可保證進一步的准確性。接下來還有以下兩件事,如例2中所示:

  ·假如被跟蹤的堆內存範圍內任一部分被提交,必須更新你的跟蹤列表。

  ·否則,刪除列表中的元素並跟蹤新的範圍。

  假如提交的部分在中間,那麽就有可能把堆內存範圍截成好幾斷,總而言之,在你放棄跟蹤老的範圍之前,應先在全範圍內檢查一下哪一部分仍處于提交狀態。

  程序中可能會用到好幾個不同的堆,在Windows上,假如你調用HeapCreate()並把返回的句柄傳給接下來的HeapAlloc()、HeapReAlloc()、HeapFree()函數調用時,就會創建一些不同的堆;另外,假如你加載了C運行時庫DLL的多個實例,也會因爲每個實例使用它們自己的堆而産生多個堆。此時,可在不同的列表中跟蹤多個堆,也可在不同的列表中跟蹤它們的內存塊。首先,這樣做的好處是,列表的查找可以變得很快,其次,當堆被"摧毀"時,你可以毫無顧慮地清除跟蹤列表,但這需要在堆創建和釋放時加入額外的代碼來完成--即分別設置和刪除對應的列表。另外,你也可治理一個堆內存塊的列表及一個堆範圍的列表,並在程序開始運行時建立它們。

  一些專業的運行時分析工具也能對分配的內存塊及堆範圍進行跟蹤,就像前面所說的自定義內存分配函數與釋放函數一樣,甚至還能通過基本的虛擬內存HASH值(ox187d690)進一步跟蹤,以便爲精確的運行時錯誤檢測提供更加可靠的手段。但此處描述的方法並不足以幫助你理解何時才能找到通過程序可控制的堆內存塊,減少虛擬內存消耗的時機。

  找到適當的清理時機

  爲使用跟蹤信息以精確定位那些導致虛擬內存不必要增長的堆內存塊,你還必須要記錄下內存訪問動作,並在程序讀寫堆內存時,標記出相應的內存塊跟蹤結構。

  

   假如你的堆內存塊都是通過存取函數訪問的,那麽很輕易就可找到所有代碼讀寫的內存部分,並以條件編譯生成一個特定的版本。這些存取函數可查找你列表上被訪問過的內存塊,並設置布爾值作出標記。

  當虛擬內存被提交生成一個堆,你的自定義內存分配函數應取消堆中所有內存塊的標記,而在接下來,它們可能會重新被標記上,一個接一個,就像你的程序正在訪問它們。當一個取消標記的內存塊被釋放時,相應的虛擬內存也會被釋放,此時你自定義的釋放函數就會先一步釋放內存塊,以減小虛擬內存的覆蓋範圍。

  也可安排對堆內存塊作一些臨時的掃描,這也許可在每一次虛擬內存提交時進行。假如一個內存塊在經過多遍掃描後仍保持未標記狀態(也許會花很長時間),則包含此內存塊的虛擬內存範圍就必須對所跟蹤的內存塊數進行檢查。假如那個內存塊,或者一組被忽視的類似內存塊,只是單獨地與提交的虛擬內存有關系,那麽通過釋放與重新定位這些內存塊,你可能已經找到一個減少程序虛擬內存要求的好方法。

  假如你實現了此處描述的所有內存塊和堆範圍跟蹤代碼,而當這些所有的跟蹤都起作用時,那麽程序的速度將會變得很慢,主要是因爲在每一次堆內存訪問時,都會進行一遍列表查找,當然,也可以通過一些快速列表查找方法如二分法查找、跳躍查找之類的來縮短查找的時間,還可使用對應每個堆的單獨列表來加速查找。假如程序使用了許多的堆內存塊,並且也找到了減少額外虛擬內存消耗的方法,以往所花費的所有精力與耐心,與此時得到的回報相比,就算不上什麽了。
 
 
 
上一篇《淺議C++ 中的垃圾回收方法》
下一篇《C++Builder集成開發環境概述》
 
 
 
 
 
 
日版寵物情人插曲《Winding Road》歌詞

日版寵物情人2017的插曲,很帶節奏感,日語的,女生唱的。 最後聽見是在第8集的時候女主手割傷了,然後男主用嘴幫她吸了一下,插曲就出來了。 歌手:Def...

兄弟共妻,我成了他們夜裏的美食

老鍾家的兩個兒子很特別,就是跟其他的人不太一樣,魔一般的執著。兄弟倆都到了要結婚的年齡了,不管自家老爹怎麽磨破嘴皮子,兄弟倆說不娶就不娶,老父母爲兄弟兩操碎了心...

如何磨出破洞牛仔褲?牛仔褲怎麽剪破洞?

把牛仔褲磨出有線的破洞 1、具體工具就是磨腳石,下面墊一個硬物,然後用磨腳石一直磨一直磨,到把那塊磨薄了,用手撕開就好了。出來的洞啊很自然的。需要貓須的話調幾...

我就是掃描下圖得到了敬業福和愛國福

先來看下敬業福和愛國福 今年春節,支付寶再次推出了“五福紅包”活動,表示要“把欠大家的敬業福都還給大家”。 今天該活動正式啓動,和去年一樣,需要收集“五福”...

冰箱異味産生的原因和臭味去除的方法

有時候我們打開冰箱就會聞到一股異味,冰箱裏的這種異味是因爲一些物質發出的氣味的混合體,聞起來讓人惡心。 産生這些異味的主要原因有以下幾點。 1、很多人有這種習...

《極品家丁》1-31集大結局分集劇情介紹

簡介 《極品家丁》講述了現代白領林晚榮無意回到古代金陵,並追隨蕭二小姐化名“林三”進入蕭府,不料卻陰差陽錯上演了一出低級家丁拼搏上位的“林三升職記”。...

李溪芮《極品家丁》片尾曲《你就是我最愛的寶寶》歌詞

你就是我最愛的寶寶 - 李溪芮 (電視劇《極品家丁》片尾曲) 作詞:常馨內 作曲:常馨內 你的眉 又鬼馬的挑 你的嘴 又壞壞的笑 上一秒吵鬧 下...

烏梅的功效與作用以及烏梅的食用禁忌有哪些?

烏梅,又稱春梅,中醫認爲,烏梅味酸,性溫,無毒,具有安心、除熱、下氣、祛痰、止渴調中、殺蟲的功效,治肢體痛、肺痨病。烏梅泡水喝能治傷寒煩熱、止吐瀉,與幹姜一起制...

什麽是脂肪粒?如何消除臉部脂肪粒?

什麽是脂肪粒 在我們的臉上總會長一個個像脂肪的小顆粒,弄也弄不掉,而且顔色還是白白的。它既不是粉刺也不是其他的任何痘痘,它就是脂肪粒。 脂肪粒雖然也是由油脂...

網絡安全治理:國家安全保障的主要方向是打擊犯罪,而不是處置和懲罰受害者

來源:中國青年報 新的攻擊方法不斷湧現,黑客幾乎永遠占據網絡攻擊的上風,我們不可能通過技術手段杜絕網絡攻擊。國家安全保障的主要方向是打擊犯罪,而不是處置和懲罰...

河南夫妻在溫嶺網絡直播“造人”內容涉黃被刑事拘留

夫妻網絡直播“造人”爆紅   1月9日,溫嶺城北派出所接到南京警方的協查通告,他們近期打掉了一個涉黃直播APP平台。而根據掌握的線索,其中有一對涉案的夫妻主播...

如何防止牆紙老化?牆紙變舊變黃怎麽辦?

如何防止牆紙老化? (1)選擇透氣性好的牆紙 市場上牆紙的材質分無紡布的、木纖維的、PVC的、玻璃纖維基材的、布面的等,相對而言,PVC材質的牆紙最不透氣...

鮮肌之謎非日本生産VS鮮肌之謎假日貨是謠言

觀點一:破日本銷售量的“鮮肌之謎” 非日本生産 近一段時間,淘寶上架了一款名爲“鮮肌之謎的” 鲑魚卵巢美容液,號稱是最近日本的一款推出的全新護膚品,産品本身所...

中國最美古詩詞精選摘抄

系腰裙(北宋詞人 張先) 惜霜蟾照夜雲天,朦胧影、畫勾闌。人情縱似長情月,算一年年。又能得、幾番圓。 欲寄西江題葉字,流不到、五亭前。東池始有荷新綠,尚小如...

關于女人的經典語句

關于女人的經典語句1、【做一個獨立的女人】 思想獨立:有主見、有自己的人生觀、價值觀。有上進心,永遠不放棄自己的理想,做一份自己喜愛的事業,擁有快樂和成就...

未來我們可以和性愛機器人結婚嗎?

你想體驗機器人性愛嗎?你想和性愛機器人結婚嗎?如果你想,機器人有拒絕你的權利嗎? 近日,第二屆“國際人類-機器人性愛研討會”大會在倫敦金史密斯大學落下帷幕。而...

全球最變態的十個地方

10.土耳其地下洞穴城市 變態指數:★★☆☆☆ 這是土耳其卡帕多西亞的一個著名景點,傳說是當年基督教徒們爲了躲避戰爭而在此修建。裏面曾住著20000人,...

科學家稱,人類死亡後意識將在另外一個宇宙中繼續存活

據英國《每日快報》報道,一位科學家兼理論家Robert Lanza博士宣稱,世界上並不存在人類死亡,死亡的只是身體。他認爲我們的意識借助我們體內的能量生存,而且...

《屏裏狐》片頭曲《我愛狐狸精》歌詞是什麽?

《我愛狐狸精》 - 劉馨棋   (電視劇《屏裏狐》主題曲)   作詞:金十三&李旦   作曲:劉嘉   狐狸精 狐狸仙   千年修...

 
 
 
  在今天,已有許多的32位應用程序感到,在32位平台上可用的虛擬內存受到了一定的限制,對程序開發者來說,即使是開始關注64位平台時,也不得不維護軟件的32位版本,這就需要一種方法,以使代碼的兩個版本都保持相當的可伸縮性。   目前的內存剖析工具能幫助確定,當程序達到峰值內存使用量時,都發生了什麽,但是這些工具都過于關注已分配的內存塊,而不是已提交的虛擬內存地址空間,而這兩種衡量標准沒有直接的相關性,如內存泄漏、內存碎片、內存塊內的空間浪費、或過度延遲的內存單元重新分配這些因素,都會導致不必要的虛擬內存提交。運行時分析工具如IBM Rational Purify或Parasoft Inuse均可以提供內存泄漏及已用內存的描述,這些信息是非常有用的,但是,一個非凡的內存塊也許可能、也許不可能影響到虛擬內存覆蓋區,另外,甚至一個有碎片的內存堆中的一個小塊,也能直接影響到虛擬內存覆蓋區。從另一方面來說,在此範圍內的任意內存塊--甚至泄漏的塊,對虛擬內存覆蓋區來說,也不會與之有什麽關系,除非每一個此範圍內有用的內存塊能重新分配到一個更緊湊的範圍內,這就有點像Java或托管程序的垃圾回收機制,但對大多數C/C++本地應用程序來說,就絕對不可能了,因爲在虛擬內存空間中,它們內存塊的位置是不確定的。   至于本地代碼,不必要的虛擬內存使用,這個實際的問題,比未清理的內存塊這個理論上的問題,更加有實質性。未清理的內存塊可能導致虛擬內存的浪費,造成過多的系統開銷,但或者不會;這完全依靠于堆治理器是否提交了更多的虛擬內存,以支撐這種浪費。某些很小的未使用的內存塊,不會引起不必要的堆"擴展"。與其讓你來猜哪一個或多少已浪費的內存塊導致了堆擴展,倒不如學會怎樣判定出有意義的浪費是什麽。當堆中包含不再使用的內存塊時,此時通過加入對未縮減堆的檢查,就能確定出與你的程序虛擬內存要求有很大關系的、必須進行的內存塊清理。   爲找出哪一個堆中的內存塊需多留意,必須在程序中加入一些額外的代碼,以跟蹤內存堆範圍及已分配的內存塊。對額外的代碼進行條件編譯,生成一個特定的版本,也許是一個不錯的辦法。   爲達到此目的,需編寫自定義的內存分配例程,並跟蹤每一個內存塊,另有一個自定義的釋放例程,且跟蹤虛擬內存中堆的位置,請參見例1與例2的僞代碼算法。可能還需編寫自定義的訪問函數以標記出訪問過的內存塊,以便于在適當的時候釋放虛擬內存,所有這些並不需要過多的內存開銷。另一方面,假如你的程序以堆的形式使用了大量的內存,那麽將會極大地降低性能,此處的方法也不是長久之計。   例1: /* 輸入參數*/ 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 /* 一個現有的堆範圍被擴展 */ 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中有一個新的堆範圍 */ 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   例2: /* 輸入參數 */ ADDRESS triggerAddr SIZE triggerSize LIST a list of tracked heap ranges /* 局部變量 */ ADDRESS origRangeBase SIZE origRangeSize BOOL bFoundChange bFoundChange = FALSE IF (the virtual memory at triggerAddr is not tracked on the heap range list as part of a heap range) DO /*似乎我們已經清楚此次釋放了。*/ END ELSE IF (an Access exception occurs when memory at triggerAddr is read) DO bFoundChange = TRUE END IF (bFoundChange) DO /*因爲之前內存塊占用的空間被釋放了,所以堆占用的虛擬內存範圍就改變了。*/ make system calls to determine the bases and extents of the tracked committed heap ranges in the immediate vicinity of the decommitted range that includes the addresses from triggerAddr to (triggerAddr + triggerSize) /*更新堆範圍跟蹤,以反映剩余提交的範圍 */ IF (any portion of the tracked heap range that contained the block at TriggerAddr is still committed) DO update the heap range list to track just the portion(s) of that range that remain committed END ELSE DO delete the list element that tracks the range END END QQread.com 推出Windows2003教程 win2003安裝介紹 win2003網絡優化 win2003使用技巧 win2003系統故障 服務器配置 專家答疑 更多的請看:http://www.qqread.com/windows/2003/index.Html跟蹤堆內存塊   可使用自定義的內存分配函數來進行內存塊的跟蹤,而這種函數最初被稱爲普通內存分配函數,舉例來說,C語言程序中一般使用malloc(),爾後,自定義的內存分配會進行以下一系列的操作:   ·在目前已分配的內存塊列表中,跟蹤新分配的內存塊。   ·決定是否向系統提交虛擬內存。   ·假如虛擬內存已被提交,跟蹤包含此內存塊的堆範圍,並更新上述堆內存塊列表,以標識出從未被訪問過的內存塊。   還需要自定義的釋放與重分配內存函數,以便通過程序中使用的內存塊的地址與大小,來更新內存塊列表。所跟蹤的堆內存塊列表應包含如下結構:   ·內存塊的基地址。   ·自身大小   ·用于指示自從上次虛擬內存被提交之後,內存塊是否被訪問過的布爾值。   當一個內存塊被釋放後,自定義的釋放代碼將會進行以下操作:   ·假如自從上次堆擴展之後,內存塊還未被訪問過,將向程序報告。   ·從列表中刪除跟蹤的內存塊。   ·判定系統是否已釋放了包含此內存塊的虛擬內存。   ·假如虛擬內存被釋放,要相應地更新,以反映剩余的堆範圍。   當一個內存塊被重新分配時,你的自定義重新分配內存代碼必須進行以下兩種操作:首先,在釋放之後重新跟蹤內存塊,因爲重新分配的內存塊可能不在原位置;其次,在分配之後也要重新跟蹤新的基地址及分配內存塊的大小。另有一個可選的方法,你可檢查是否重新分配的內存塊被移動了,假如沒有被移動,只需僅僅更新內存塊跟蹤列表,標出此內存塊的大小;假如內存塊還在同一基地址,但是增長了,此時就要檢查堆擴展,並按照前述分配內存的方法重來一遍。   跟蹤堆自身   堆跟蹤取決于當內存塊被分配或釋放時,虛擬內存是否分別被提交或釋放,依此可以建立一張堆內存範圍跟蹤表,以確定在程序運行期間,虛擬內存空間中堆的確切位置,跟蹤列表中應包括如下數據:   ·跟蹤範圍的基地址   ·自身大小   在Windows操作系統中,這些值可通過HeapWalk()調用獲得,此處要注重的是,HeapWalk()函數調用開銷巨大,因此,只在程序需要時調用,而不是當有內存分配或釋放時都調用。另一種Windows上的方法是使用IsBadReadPointer()函數,當一個內存塊被釋放後,你可以調用這個函數快速地判定包含此內存塊的虛擬內存是否已被釋放。另一個可以跨平台的備選方法是,可試著訪問包含此內存塊的虛擬內存,並捕捉可能發生的訪問異常。此外,只有在一種情況下會考慮使用如HeapWalk()這樣開銷巨大的函數,就是需判定鄰近剩余的已提交堆範圍。   通過一種探測堆內存提交的算法,堆內存跟蹤列表會不斷地增長,如例1中所示。要注重的是,當你的程序分配一個內存塊時,自定義的內存分配代碼也能跟蹤到這些內存塊,並使用例1中的算法來更新包含內存塊的堆列表。假如一個內存塊的分配導致了額外的虛擬內存被提交,那麽被內存塊占用的虛擬內存會在之前就釋放,或許之前就被用作別的用途。在任一情況中,必須有條件地更新堆範圍跟蹤列表: l 假如正在跟蹤已提交範圍的基地址,此時必須更新範圍的大小,以指示出新的範圍上限。   ·否則,必須建立一張新的堆內存範圍跟蹤表。   假如一個新的內存塊出現在一個之前未被跟蹤的堆範圍中,就滿足了以上條件,此時明智地使用前述的系統調用可高效地跟蹤堆內存範圍。   當你的程序釋放一個內存塊時,自定義的內存釋放代碼會使用到如例2中的算法,此算法會先判定釋放的內存是否與被跟蹤的堆內存範圍有關;接下來,必須檢查已釋放內存塊占用的空間是否仍處于提交狀態,假如是,表明了即使內存塊被釋放,虛擬內存的覆蓋區也沒有發生改變,否則,你的代碼必須進行如例2結尾處的系統調用--如Windows中的HeapWalk()--以確保跟蹤的堆是最新的,且包含堆內存塊的虛擬內存已被釋放。   假如虛擬內存已經被提交,那麽你應該檢查那些通常包含了最近被釋放的堆內存塊的內存範圍,以確認是否有此範圍內的內存被提交,而此範圍內任何被提交的內存部分都應該是在一個堆內存範圍內,非凡是假如它包含了跟蹤列表上的內存塊,進行此檢查可保證進一步的准確性。接下來還有以下兩件事,如例2中所示:   ·假如被跟蹤的堆內存範圍內任一部分被提交,必須更新你的跟蹤列表。   ·否則,刪除列表中的元素並跟蹤新的範圍。   假如提交的部分在中間,那麽就有可能把堆內存範圍截成好幾斷,總而言之,在你放棄跟蹤老的範圍之前,應先在全範圍內檢查一下哪一部分仍處于提交狀態。   程序中可能會用到好幾個不同的堆,在Windows上,假如你調用HeapCreate()並把返回的句柄傳給接下來的HeapAlloc()、HeapReAlloc()、HeapFree()函數調用時,就會創建一些不同的堆;另外,假如你加載了C運行時庫DLL的多個實例,也會因爲每個實例使用它們自己的堆而産生多個堆。此時,可在不同的列表中跟蹤多個堆,也可在不同的列表中跟蹤它們的內存塊。首先,這樣做的好處是,列表的查找可以變得很快,其次,當堆被"摧毀"時,你可以毫無顧慮地清除跟蹤列表,但這需要在堆創建和釋放時加入額外的代碼來完成--即分別設置和刪除對應的列表。另外,你也可治理一個堆內存塊的列表及一個堆範圍的列表,並在程序開始運行時建立它們。   一些專業的運行時分析工具也能對分配的內存塊及堆範圍進行跟蹤,就像前面所說的自定義內存分配函數與釋放函數一樣,甚至還能通過基本的虛擬內存HASH值(ox187d690)進一步跟蹤,以便爲精確的運行時錯誤檢測提供更加可靠的手段。但此處描述的方法並不足以幫助你理解何時才能找到通過程序可控制的堆內存塊,減少虛擬內存消耗的時機。   找到適當的清理時機   爲使用跟蹤信息以精確定位那些導致虛擬內存不必要增長的堆內存塊,你還必須要記錄下內存訪問動作,並在程序讀寫堆內存時,標記出相應的內存塊跟蹤結構。 假如你的堆內存塊都是通過存取函數訪問的,那麽很輕易就可找到所有代碼讀寫的內存部分,並以條件編譯生成一個特定的版本。這些存取函數可查找你列表上被訪問過的內存塊,並設置布爾值作出標記。   當虛擬內存被提交生成一個堆,你的自定義內存分配函數應取消堆中所有內存塊的標記,而在接下來,它們可能會重新被標記上,一個接一個,就像你的程序正在訪問它們。當一個取消標記的內存塊被釋放時,相應的虛擬內存也會被釋放,此時你自定義的釋放函數就會先一步釋放內存塊,以減小虛擬內存的覆蓋範圍。   也可安排對堆內存塊作一些臨時的掃描,這也許可在每一次虛擬內存提交時進行。假如一個內存塊在經過多遍掃描後仍保持未標記狀態(也許會花很長時間),則包含此內存塊的虛擬內存範圍就必須對所跟蹤的內存塊數進行檢查。假如那個內存塊,或者一組被忽視的類似內存塊,只是單獨地與提交的虛擬內存有關系,那麽通過釋放與重新定位這些內存塊,你可能已經找到一個減少程序虛擬內存要求的好方法。   假如你實現了此處描述的所有內存塊和堆範圍跟蹤代碼,而當這些所有的跟蹤都起作用時,那麽程序的速度將會變得很慢,主要是因爲在每一次堆內存訪問時,都會進行一遍列表查找,當然,也可以通過一些快速列表查找方法如二分法查找、跳躍查找之類的來縮短查找的時間,還可使用對應每個堆的單獨列表來加速查找。假如程序使用了許多的堆內存塊,並且也找到了減少額外虛擬內存消耗的方法,以往所花費的所有精力與耐心,與此時得到的回報相比,就算不上什麽了。
󰈣󰈤
 
 
 
  免責聲明:本文僅代表作者個人觀點,與王朝網路無關。王朝網路登載此文出於傳遞更多信息之目的,並不意味著贊同其觀點或證實其描述,其原創性以及文中陳述文字和內容未經本站證實,對本文以及其中全部或者部分內容、文字的真實性、完整性、及時性本站不作任何保證或承諾,請讀者僅作參考,並請自行核實相關內容。
 
 
小龍女彤彤之情溢皇都
龔潔
智能手機形象美女
崔潔彤
回家的路上----
中國一站(哈爾濱)
清明植物園的花。
桃花堤印象之豎版
 
>>返回首頁<<
 
 熱帖排行
 
 
 
 
© 2005- 王朝網路 版權所有