天天看點

讓我生不如死的Windows CE記憶體洩漏【轉載】

        作者:IT168 潘少紅責任編輯:McLaren

        很多實時嵌入式裝置是長時間不間斷運作的,即使是少許的記憶體洩漏,也會積少成多,對嵌入式系統帶來災難性的影響。這幾天,我在嵌入式軟體項目中就飽嘗到這個痛苦,讓我明白到嵌入式實時系統的應用軟體也會有許多記憶體問題,進而導緻嵌入式系統的崩潰。例如非法的記憶體通路、各種死鎖以及諸如堆棧溢出、數組越界和記憶體洩漏等。

  Windows CE作為最流行的一種嵌入式作業系統,現正廣泛被應用。我所負責的嵌入式應用程式也是在Windows CE平台上開發的。在進入測試階段中,我發現有一個程式子產品系統記憶體和CPU資源消耗急劇增加,持續增長到出現OutOfMemoryError為止,然後自動重新開機。這個問題折騰到我生不如死,痛苦不堪。花了我好幾個通宵達旦的加班後,經過分析終于确認Windows CE記憶體洩漏是造成這次Windows CE系統崩潰的主要原因。這裡與大家分享我在開發過程中遇到的記憶體洩漏的檢測和處了解決過程。

  一.Windows CE如何進行記憶體配置設定?

  為了判斷是否有記憶體洩露,我們首先需要了解Windows CE是如何管理記憶體的。許多嵌入式程式員都有一個共識,就是如果評選在Windows CE 程式中遇到最多的問題,那其中一個問題一定有記憶體問題。

  (1)什麼是Windows CE記憶體管理

  一般來說,運作Windows CE的嵌入式裝置出于緊湊型的考慮記憶體都不大,以至于有時候有些程式員會為了節省記憶體開支而犧牲程式的某些性能。但盡管WinCE系統的記憶體很小,用來管理記憶體的函數卻十分完善。Windows CE實作了Windows XP中幾乎全部的Win32記憶體管理API。例如,Windows CE支援虛拟記憶體配置設定,本地和分離的堆管理,甚至還有記憶體映射檔案。像Windows XP一樣,Windows CE支援帶有應用程式間記憶體保護功能的32位位址空間,這一點對于多程式和多線程運作時是非常重要的功能。但是Windows CE畢竟是被設計來應用于實時場合的,是以它底層的記憶體結構又不同于Windows XP。

  Windows CE核心可以在Flash上直接運作,也可以加載到記憶體中運作。Flash的運作方式,是把核心的可執行映像燒寫到Flash上,系統啟動時從Flash的某個位址開始執行。在這種情況下,Windows CE系統就像直接讀硬碟,存儲在Flash上的程式能夠以現場執行的方式運作。這種能力對小型系統來說使之在具有巨大的優勢,這樣這能快速啟動一個應用程式,是以這種方法被很多嵌入式系統所采用。另一種是核心加載方式,是把核心的壓縮檔案存放在Flash上,系統啟動時讀取壓縮檔案在記憶體裡解壓,然後開始執行。

  (2)虛拟記憶體和函數應用

  和大多數現代作業系統一樣,Windows CE實作按需調頁的虛拟記憶體機制。由于Windows CE系統使用了虛拟記憶體,這就給應用程式造成了一個假象,以為計算機安裝的記憶體遠遠超過自己所需要的數量。Windows CE是32位的作業系統,是以支援4GB的虛拟位址空間。Windows把這些位址空間分給程序和系統使用,每個部分可以獲得2GB的虛拟記憶體。

  虛拟記憶體是記憶體類型中最基礎的。Windows CE 實作了系統的虛拟記憶體管理,在一個虛拟記憶體系統中,應用程式主要處理這個虛拟的位址空間,并不涉及到由硬體管理的實體記憶體。系統調用虛拟記憶體API來為其它類型記憶體配置設定記憶體,包括堆和棧。Windows CE虛拟記憶體頁可以處在三種狀态:自由(free),保留(reserved),或被送出(committed)。

  簡單說,就是當一個應用程式要查詢系統的記憶體時,可使用虛拟記憶體API,包括VirtualAlloc,VirtualFree和VirtualReSize函數,這些函數可以直接操作虛拟記憶體空間的虛拟記憶體頁面。例如,頁面可以保留,送出給實體記憶體,或使用這些函數釋放。Windows CE實作了Win32的GetSystemInfo和GlobalMemoryStatus函數。另一個檢測系統狀态的函數是:void GlobalMemoryStatus(LPMEMORYSTATUS lpmst),通過GlobalMemoryStatus傳回的資訊可以驗證Windows CE記憶體結構。

  (3)釋放虛拟記憶體

  不同于Windows XP,Windows CE隻支援在堆中配置設定固定(fixed)的塊。這簡化了記憶體塊在堆中的處理,但是這使得堆在配置設定和釋放一段時間後會産生碎片。當堆裡已經清空的時候,仍然會占用大量的虛拟記憶體頁,因為系統不能在堆中記憶體頁沒有完全釋放的時候回收這些頁。這時,一般情況下是可以通過調用VirtualFree來取消送出,或釋放虛拟記憶體。從實體RAM頁中取消送出或者取消映射,但是保持頁被保留的狀态,當在區域中的所有的頁通過VirtualFree被釋放時,也應該處在同樣的情況下。更确切地說,區域中的全部頁要被釋放,那這些頁要麼都是被送出的頁,要麼都是被保留的頁。如果有些頁被送出,有些頁被保留,那麼VirtualFree函數調用就會失敗。

  實際上,Windows CE會監視系統自由的記憶體,并對越來越少的記憶體作出響應。當很少記憶體可用時,Windows CE首先發送WM_HIBERNATE消息,接下來會限制可能的記憶體配置設定。當應用程式被發送了一個WM_HIBERNATE消息後,系統将檢測記憶體級别,确認是否可用記憶體在限度之上,如果可用記憶體不足,WM_HIBERNATE消息将被發送給下一個程式,這會持續到所有程式被發送了WM_HIBERNATE消息。

  二. 什麼是Windows CE記憶體洩露

  雖然Windows CE有許多方法來管理系統記憶體的運作,但還是有可能發生記憶體錯誤的。Win32程式設計中常見記憶體錯誤:①記憶體配置設定錯誤;②使用未初始化的記憶體;③記憶體洩露;④使用已經釋放的記憶體資源。

  (1)什麼是記憶體洩漏

  記憶體洩漏是指程式在運作過程中申請的記憶體,在程式結束時沒有被釋放。我們常說的記憶體洩漏是指堆記憶體的洩漏,堆記憶體是指程式從堆中配置設定的。一般來說,應用程式是使用從堆中配置設定到一塊記憶體,使用完後程式必須負責相應的釋放該記憶體塊。否則,這塊記憶體就不能被再次使用,我們就說這塊記憶體洩漏了。

  一般來說,在所有時刻Windows CE記憶體管理器都知道程序所擁有的實體記憶體和虛拟記憶體。然而,如果程序配置設定記憶體時但由于Bug而無法釋放記憶體(記憶體洩漏),記憶體管理器就可能無法了解這些已配置設定的記憶體,也無法重新通路這些記憶體,而必須等到程序退出時回收記憶體。但需要特别注意的是,同樣的程式在Window XP平台上可能沒有什麼問題,但在缺乏記憶體的Windows CE平台,經過長時間運作該程式可能會記憶體耗盡而導緻系統重新開機,這是我在經過幾個生不如死的通宵達旦測試後得到的寶貴經驗和教訓。

  是以,記憶體洩漏引發的性能失常完全不同于程式錯誤,這些問題很難通過調試器對代碼進行單步調試加以解決。對于将會在某時刻退出的桌面應用程式,較小的記憶體洩漏是可以承受的,因為退出程序将把占用的所有記憶體返還給作業系統。但對于長時間運作的嵌入式系統,則通常需要確定絕對沒有記憶體洩漏。

  (2)常見的記憶體洩漏原因

  常見的記憶體洩漏有這幾種原因:①Windows CE記憶體碎片。②在局部堆申請的堆隻增加不會馬上減少,直到程式退出。③程式運作時配置設定實體記憶體,當程式使用完後,這些實體記憶體仍然被占用,直到系統記憶體不足時分頁記憶體交換到分頁檔案中,然後才釋放掉其占用的實體記憶體。④Windows CE記憶體管理的缺陷。

  總而言之,記憶體洩漏産生的主要原因是保留了卻不再使用的記憶體空間。Windows CE雖然有自動管理記憶體的功能,但記憶體洩漏也是不容忽視,它往往是破壞嵌入式系統穩定性的重要因素。

  三. 如何檢測和處理記憶體洩漏?

  如何查找引起記憶體洩漏的原因,一般有兩個步驟:第一是安排有經驗的程式設計人員對代碼進行走查和分析,找出記憶體洩漏發生的位置。第二是使用專門的記憶體洩漏測試工具進行測試。

  (1)代碼走讀檢測記憶體洩漏

  通常在懷疑發生記憶體洩漏之後,第一步是要弄清楚洩漏了什麼資料和引起了什麼洩漏。一般說來,一個正常的系統在其運作穩定後其記憶體的占用量是基本穩定的,不應該是無限制的增長的。根據這樣的基本假設,我們持續地觀察系統運作時使用的記憶體的大小,如果記憶體的大小持續地增長,則說明系統存在記憶體洩漏。

  記憶體洩漏可通過代碼走讀來發現和定位,也可以用專用的工具來測試和定位。實際上,對于記憶體洩漏,代碼檢查有時能比采用任何技術解決方案更快地找到問題所在。預防記憶體洩漏的唯一方法就是要求程式員在程式結束時,必須将每個申請的記憶體都釋放。

  (2)使用工具檢測記憶體洩漏

  一旦知道确實發生了記憶體洩漏,就需要更專業的工具來查明為什麼會發生洩漏。在這個時候,我們通常需要使用一些開銷較低的工具來監控和查找記憶體洩漏。查找記憶體洩漏的工具很多,最常用的釋放工具就是dmalloc和mpatrol,這些工具提供了記錄并檢查所有記憶體配置設定的調試版堆棧,進而有利于分析記憶體洩漏和懸挂指針。

  檢測記憶體洩漏的關鍵是要能截獲住對配置設定記憶體和釋放記憶體的函數的調用,當截獲住這兩個函數,我們就能跟蹤每一塊記憶體的生命周期。比如,每當成功的配置設定一塊記憶體後,就把它的指針加入一個全局的list中;每當釋放一塊記憶體,再把它的指針從list中删除。這樣,當程式結束的時候,list中剩餘的指針就是指向那些沒有被釋放的記憶體。哪麼,最簡單的記憶體洩漏檢測方式就是截獲住這些指針。

  總的來說,無論那種方式,我們都需要認真檢查應用程式任何記憶體配置設定調用的傳回代碼,因為在Windows CE中比在桌面版本的Windows中有更多的機會導緻記憶體配置設定失敗,進而會導緻記憶體洩漏。

備注:轉自天極網,URL=http://dev.yesky.com/346/8269346.shtml

繼續閱讀