2011-03-10 15 views
7

我有一個GUI應用程序,有內存泄漏。我已經通過FastMM在很多測試周期中證實了這一點。 在一個特定客戶端的服務器上,我得到隨機崩潰。服務器規格與我們其他客戶的規格完全一致(並且我們實際上已經嘗試了各種硬件),程序使用的文件也是如此(據我所知,有一些超級敏感的材料,我無法訪問,但似乎沒有任何不尋常的事情)。德爾福 - 檢查內存被釋放「準時」

我嘗試過EurekaLog和MadShi這樣的可能縮小問題的範圍,但不幸的是,它們似乎只在碰撞時偶爾發生異常,而不是全部。當它發生時,通常會在崩潰之前顯示一個或多個「內存不足」錯誤。

所以我想,也許某些對象得到釋放「爲時已晚」,即只有當應用程序被關閉,而不是當我的意思是釋放他們?我已經看到了FastMMUsageeTracker演示,但並沒有真正理解這一切。有沒有文件的地方?或者有人可能會提供(有點容易理解)的話,我該如何去檢查這個問題?

或者,什麼是檢測應用程序已接近「內存限制」,以便採取一些預防措施,最好的辦法?如果我理解正確,一個普通的Delphi應用程序是32位,它應該很好地處理高達2Gb的內存(當然硬件支持它),對嗎?

PS:德爾福2009年或XE,如果是相關

謝謝!

編輯 - 問題可能解決

我們能夠找到一個問題,即封閉,並正在以更快的速度創造了一段時間後比它消失將自動釋放自己的彈出窗口。隨着時間的推移,這將會消耗大量的內存,然後任何內存分配都會基本上導致其超出邊緣並觸發「內存不足」問題。

這將解釋爲什麼堆棧跟蹤不一致。

我並不完全相信這是我們唯一的問題,因爲,雖然不太可能,這種情況下很可能之前已經在我們的應用程序已經運行多年的事,但不知何故,沒有。我會在這個問題上做更多的探索。

感謝所有回覆的人,每個答案其實都有寶貴的信息。

+1

PS內存不足異常可能發生在大約1 GB以上的任何位置 - 沒有預定義的級別。有很多因素似乎影響了確切的閾值:總RAM,虛擬內存總量,其他進程使用了​​多少等。另外,我的代碼最初來自FastMM內存跟蹤器! – Misha 2011-03-10 03:57:50

+1

值得注意的一件事:我從來沒有設法耗盡我工作過的項目中的可用內存,但是我幾次發現內存不足。在某些情況下,由於損壞或計算得不好的數據可能會導致內存管理器在您真正需要的幾K或更少的數量時分配幾GB的單個緩衝區。 – 2011-03-10 04:40:47

回答

7

如果你有德爾福XE,它配備了AQTime和AQTime有一個內存分配分析器作爲其招數袋的一部分。如果您在程序中運行該程序,則可能會看到RAM的存儲位置。

+1

謝謝梅森。我嘗試過(我對它有很大的期望),並且我也嘗試過Pro版本的試用。出於某種原因,我似乎無法讓分配分析器工作。相反,它似乎在我要求它獲得結果時就掛起了(我在今天讓它「獲得結果」超過一個小時 - 如果相關的話,應用程序的工作集大約爲100Mb,並且從未超過「試圖獲得結果」消息)。它與獨立版本和集成版本完全相同。我可以讓性能分析器在沒有問題的情況下工作,但不是分配問題。 – Bourgui 2011-03-10 01:38:03

+0

更新:我設法使用了獨立的專業版 - 我在構建應用程序時忘記生成堆棧跟蹤,現在我可以使用分配分析器。它指向我一個被稱爲數百或數千次的定時彈出窗口,速度太快而不能自動關閉。雖然(???)仍然無法從IDE集成版本中獲得結果。 – Bourgui 2011-03-10 16:17:11

+0

標記爲答案,因爲這是我真正發現彈出問題的方式。 – Bourgui 2011-03-10 16:51:04

3

您可以找到您的應用程序使用多少內存 - 看到這個About頁。簡介:

uses PsAPI; 

//current memory size of the current process in bytes 
function CurrentMemoryUsage: Cardinal; 
var 
    pmc: TProcessMemoryCounters; 
begin 
    pmc.cb := SizeOf(pmc) ; 
    if GetProcessMemoryInfo(GetCurrentProcess, @pmc, SizeOf(pmc)) then 
    Result := pmc.WorkingSetSize 
    else 
    RaiseLastOSError; 
end; 
ShowMessage(FormatFloat('Memory used: ,.# K', CurrentMemoryUsage/1024)) ; 

如果您定期在服務器中記錄該值,您至少會知道發生了什麼。這個結果中有更多的信息可以幫助你更多地瞭解你的程序在做什麼。

解決的辦法是查看實際使用內存的情況並更積極地進行管理。我懷疑你會在什麼地方創建對象,並且只有在關閉時釋放它們,當你可以(並且應該)儘快釋放它們。

一種可能的解決方法是在完整版FastMM上使用/ 3GB開關,並查看問題是否需要更長的時間才能發生。

如果你非常不走運,你將會破壞FastMM的內存池管理算法,因此它永遠不會釋放內存(a related question)。嘗試不同的內存管理器可能會幫助你,因爲有些人會回收未使用的內存。但是,如果你正在分解你的堆,唯一真正的解決方案是制定如何避免這樣做。這是一個複雜的話題,所以再次嘗試一下上面的簡單事情。

+0

感謝Moz。我已經開始玩弄TProcessMemoryCounters,但是,這個「only」是否提供了工作集內存,也就是RAM內存?只是在這裏猜測,但不會出現「內存不足」錯誤提示它是整個虛擬內存太大?如我錯了請糾正我。我會嘗試/ 3G開關。 – Bourgui 2011-03-10 02:01:23

+0

@Bourgui,「內存不足」是一個非常廣泛的信息,現代機器應該有(多)2GB以上的物理內存,所以你不應該耗盡虛擬內存。 ProcessMemoryCounters結構中有其他信息,我會記錄整個過程並查看有哪些變化。日誌記錄還將幫助您確定哪裏出了問題。我還建議從異常情況看堆棧跟蹤。在最糟糕的情況下,如果您發現日誌記錄由於內存不足而失敗,您可能必須編寫外部記錄器。但要避免這種情況,直到你需要它。 – 2011-03-10 02:06:58

+0

感謝您的反饋意見。這也是我的想法。不幸的是,我的大多數堆棧跟蹤都沒有引導我到任何地方,而且大多數都不一致。因爲我能夠隔離問題(請參閱我的OP編輯) - 希望這是我所有問題的根源。 – Bourgui 2011-03-10 16:23:17

1

當我收到「內存不足」錯誤時,它是由於失控循環造成的。循環通常會分配內存,並不會在所有可用內存使用之前停止。釋放內存不是問題,因爲程序從來沒有去過這一點。已經咬傷了我的代碼類型有:

A「而不是x.Eof做」循環沒有「x.Next」通過數據集來推進,或

,從來沒有遇到過一個遞歸程序或子程序退出條件。

我會尋找任何類型的循環或遞歸,在某些情況下可以繼續「永遠」,如在內存中構建一個龐大的數據結構。

+0

謝謝crefird,但我不認爲這是這種情況。有關我們迄今爲止發現的更多信息,請參閱我的編輯。 – Bourgui 2011-03-10 16:50:09

6

忘掉「Windows」內存 - 你想要的是應用程序分配的實際內存。這是您可以分辨是否分配內存的唯一方法,這些內存不會隨着時間的推移而被釋放。對於德爾福2006+與FastMM,這是你所需要的:

//------------------------------------------------------------------------------ 
// CsiGetApplicationMemory 
// 
// Returns the amount of memory used by the application (does not include 
// reserved memory) 
//------------------------------------------------------------------------------ 
function CsiGetApplicationMemory: Int64; 
var 
    lMemoryState: TMemoryManagerState; 
    lIndex: Integer; 
begin 
    Result := 0; 

    // get the state 
    GetMemoryManagerState(lMemoryState); 

    with lMemoryState do begin 
    // small blocks 
    for lIndex := Low(SmallBlockTypeStates) to High(SmallBlockTypeStates) do 
     Inc(Result, 
      SmallBlockTypeStates[lIndex].AllocatedBlockCount * 
      SmallBlockTypeStates[lIndex].UseableBlockSize); 

    // medium blocks 
    Inc(Result, TotalAllocatedMediumBlockSize); 

    // large blocks 
    Inc(Result, TotalAllocatedLargeBlockSize); 
    end; 
end; 

我登錄本上的間隔(10秒至10分鐘不等),以我的日誌文件,從上次的區別在一起。

+0

+1這樣的事情也是我的第一步。如果您排除其他所有內容,請首先查明您的應用程序是否在某個列表中存儲內存,或者如果它是「真正的」堆碎片。 – 2011-03-10 09:21:36

+0

謝謝米莎,我還沒有嘗試過,但它看起來很有趣,我會給它一個鏡頭 - 在我只看着小塊之前。 – Bourgui 2011-03-10 16:18:54

2

當你得到錯誤時,你能向我們展示堆棧跟蹤嗎?錯誤發生時消耗多少內存?我在前段時間做了一個內存記錄器(用於FastMM),它記錄了2點之間的所有內存(使用「startlog」和「endlog」過程)以查找「軟泄漏」:我是列表中的分配對象但從不清除列表,只有在關閉應用程序時,FastMM纔會報告沒有泄漏。通過使用我的記憶記錄器,我可以找到那些對象(它只記錄在執行「endlog」過程之前未釋放的新分配內存)。
我會看看我是否可以找到這個代碼。

順便說一句:你可以得到一個「內存不足」,在其他3種方式:

  • FastMM當它檢測到錯誤,而分配給這個錯誤。所以不是真正的「內存不足」,而是更多的內部fastmm錯誤(由於腐敗等)
  • 分配一個非常大的塊(例如1Gb)。我曾經因爲流讀取錯誤(RemObjects)而得到這個,所以它爲一個字符串讀取錯誤的大小值,所以它試圖預先分配一個(隨機)大字符串。這樣的錯誤看起來很奇怪,因爲在我的情況下,我的應用程序已分配大約150Mb,所以也沒有真正的「內存不足」
  • 碎片:如果您嘗試分配10Mb的塊但Windows無法找到10Mb的一個連續塊,給一個「內存不足」。

所以請給出一個堆棧跟蹤和錯誤時使用的內存量!

+0

嗨安德烈,謝謝你的回覆。我沒有發佈堆棧跟蹤,因爲它們都是不同的。但是,我們可能找到了問題,請參閱我的編輯。 – Bourgui 2011-03-10 16:39:33

2

由於Delphi對32位地址空間的限制,這些問題越來越普遍。

你可以做的第一個也是最簡單的事情是在64位操作系統上運行,並從2GB的可用地址空間移動(當你在32位操作系統)4GB的地址。這不會自動發生。您需要將您的應用程序標記爲LARGEADDRESSAWARE。通過將做到這一點,下面您.dpr文件:

const 
    IMAGE_FILE_LARGE_ADDRESS_AWARE = $0020; 

{$SetPEFlags IMAGE_FILE_LARGE_ADDRESS_AWARE} 

爲內存不足錯誤的另一個常見原因是不存在的內存shorage,但你所要求的連續內存的大塊並且沒有單個連續的可用地址空間塊。

處理這個問題比較困難。您首先需要確定當前需要大量連續內存塊的代碼部分。接下來,您必須修改正在這樣做的對象,然後安排它們請求小塊內存,然後您可以「拼接在一起」以顯示較大的塊。根據我的經驗,這通常發生在使用動態數組的代碼上。

+0

謝謝大衛。問:FastMM與/ 3G標誌有什麼區別?正如我在編輯中提到的,我發現了一個問題,可能是我的麻煩來源,而且由於我的大部分內存分配都相對較小(它們不超過Mb的10倍),並且我們的應用程序正在系統上運行這不是用於其他任何事情,我懷疑(並希望!)這是它的原因。更不用說,我傾向於迴避動態數組,只要我可以。 – Bourgui 2011-03-10 16:29:03

+0

@Bourgui 10 MB的Mb,反覆可能是一個問題。/3G是32位版本。你仍然需要將你的EXE標記爲LARGEADDRESSAWARE。然後,您需要使用/ 3GB啓動Windows。有人這樣做嗎?無論如何,當然你的客戶端的服務器是64位機器。您是否將應用程序標記爲LARGEADDRESSAWARE?如果不是爲什麼不呢? – 2011-03-10 16:31:09

+0

這樣的大內存分配在應用程序中相對較少並且很遠,這只是它可以達到的最大值。他們也是持久的。這些分配也很好記錄,我們應該能夠相對容易地注意到這些問題。這就是爲什麼我不認爲這是問題。但是我還沒有完全排除任何事情。事實上,直到現在我們還沒有遇到內存問題,所以我們從來沒有動力去製作我們的應用程序LargeAddressAware,但這絕對是值得研究的東西 - 我無法忍受Delphi的64位編譯器! ; o) – Bourgui 2011-03-10 16:47:54