2012-03-28 20 views
15

我嘗試過的大多數.NET內存分析器都允許您拍攝內存快照。是否有一個.Net內存分析器將跟蹤大對象堆上的所有分配?

但是,我正在嘗試診斷一個問題,即最終導致分配給.NET的大量內存被ANTS分析器指示爲「空閒」。 (我已經證實了這個問題與其他分析器如Mem Profiler和CLR分析器

ANTS顯示我有大量的內存碎片(100%的可用內存與150MB作爲最大的塊)。堆中所有對象的大小爲180MB,我有553 MB分配給.NET,152分配給「非託管」。

但是,大對象堆(LOH)的大小僅爲175kb。我沒有分配任何最終在LOH上的對象永久存在。

因此,我的問題,一些沿線的人,我懷疑我以某種方式分配大對象(超過85k限制f或LOH),然後處理它們。我正在從數據庫(Oracle,Sql Server)讀取大量數據(估計在這裏以幾MB爲單位),將這些數據拷貝到內存中的對象數組,並將數據處理成索引(數組,字典等)以便於搜索/過濾/處理。

我的猜測是,數據讀取器臨時分配大量空間。但是,我沒有一個好方法來暫停程序並拍攝內存快照。

我想是一個跟蹤上LOH分配的每個對象的,所以我可以找出是什麼原因造成的LOH碎片和過度使用的內存(內存不返回到操作系統分析器,所以它看起來像我的進程正在花費1GB的內存來存儲200MB的分配對象。)我猜測內存不會被返回,因爲LOH沒有壓縮,所以我在所有這些內存中都卡住了我的進程,這可能是幾周(它作爲一個Windows服務運行)。

編輯:我的問題是,我的.NET應用程序正在使用大量的內存,我無法跟蹤。

編輯1:我已經使用了Visual Studio內存分析器。雖然它告訴我所有實例化的對象,總共有多少字節等,並不是給了我一個提示,告訴我爲什麼最終會獲得如此多的空閒內存。我唯一的提示/線索是ANTS告訴我的:「內存碎片限制了可分配對象的大小。」而且我有很多未使用的內存分配給.NET。

編輯2:更多配置文件顯示我有一些在LOH上分配的短期大對象。但是,分配給LOH的總金額不會超過3到4 MB。但是,在這段時間內,私人字節會穿過屋頂,翻倍和三倍,而實際分配的對象(在所有堆中)的大小隻會略微增加。例如,所有堆中的字節都是115MB,但是我的專用字節超過了512MB。

ANTS清楚地告訴我,我遇到內存碎片問題。原來我正在LOH上製造短暫的物體。但是,這些對象永遠不會超過3或4 MB。所以這些短暫的大型物體(似乎?)正在將LOH分離出來。

迴應Eric Lippert和迪士尼樂園停車場比喻(這很棒)。

這就像有人在現場停了幾分鐘,然後離開。然後保留該地點(沒有其他人可以在那裏停車),直到我重新停車!

我第一次開始調查這個時,Visual Studio警告我的內存使用情況,並建議切換到64位。 (我忘記了警告號碼,快速谷歌沒有找到它)。所以切換到x64可以緩解眼前的問題,但不能解決潛在的問題。

這就像我有一個停車場爲1000輛,但我把100輛汽車在這之後,我的泊車員尖叫,它的全...

幸運的是,我有一個巨大的VMware集羣在我的處置和理解管理員。我已經分配了8個CPU和8GB內存。所以就問題而言,我可以處理它,我只是在投入資源。另外,(正如我上面所說的)我切換到64位,因爲Visual Studio不停地嘮叨我,但我想知道它在LOH上分配了什麼,看看我是否用這種方式緩解了這種堆碎片一些小代碼的變化。也許愚蠢的差事,因爲我可以投入資源。

應用程序運行良好,偶爾出現GC暫停,速度很快。但大多數情況下,我可以忍受這種情況,我只想知道對象是什麼造成的。我的懷疑是一些短命的字典,我還沒有查到。

EDIT3http://msdn.microsoft.com/en-us/magazine/cc188781.aspx

ObjectAllocatedByClass不跟蹤大對象 堆的分配,但ObjectAllocated一樣。通過比較 這兩者的通知,一個有進取精神的靈魂應該能夠找出與正常的託管堆相對的大對象堆中的內容 。

所以它看起來像這個可以完成。然而,我的C++技能是一種生鏽的方式來挖掘這個(如果我有更多的時間,也許在將來的某個時候)。我希望分析器能夠提供這種開箱即用的功能。

+0

您的應用程序是否使用字符串interning? – 2012-03-28 16:39:48

+2

那麼具體是什麼*問題*?是虛擬內存不足,還是工作集太大,而且你打亂了頁面文件?或者是什麼? – 2012-03-28 16:40:39

+0

當你的LOH只有175kb時,碎片並不是什麼大問題。 – 2012-03-28 16:55:32

回答

0

哦,上帝 - @EricLippert是問題 - 我知道我有這個錯誤的 - 但在這裏不用:-)

LOH只會存儲特定對象的尺寸超過85K更大。我從經驗中發現,這通常是字符串 - 全局變量和/或靜態變量中的XML或大型可枚舉類型。

如果你的LOH很小,你必須將其他對象存儲在內存中,這很可能最終成爲第二代。這些可能是整個應用程序生命週期中的數組,字典等全局變量。

因此,快速檢查的第一步是檢查應用程序整個生命週期中是否存在變量。這些有成千上萬的條目嗎?其次,.NET針對每一代大小的內存大小都有一個性能計數器統計數據。計數器可以直接指向應用程序,所以這將是一個很好的下一步。

最後,GC只會在需要時清除內存。所以是的,如果GC沒有覺得需要(內存壓力明智)清除任何東西,那麼它可能會保持相同的大小。您可以強制垃圾收集,但通常建議讓GC完成其工作。

我問了一個類似的問題why a string was being stored in my application for so long.我正在使用WinDBG來分析一個崩潰轉儲,我強制轉到任務管理器 - >進程 - >右鍵單擊 - >創建崩潰轉儲。然後將其加載到WinDBG(Windows SDK 7)中。

希望鏈接能夠爲您提供更多指導來追蹤問題根源(雙關語意思)。

回到OP的問題:這是否會嚴重影響您的服務器,或者您只是好奇爲什麼內存在堅持?

+0

多米尼克,感謝您的信息。我的問題是,我沒有存儲數據。這些項目暫時只在LOH上,但分配的內存永遠都在。 – 2012-03-29 15:13:46

+0

哎呀。我真正想說的是放心,當內存需要刪除時,GC會清除它。抱歉繼續關於我的其他帖子,但我有一個非常類似的問題。當我創建崩潰轉儲時,WinDBG確實列出了LOH中的項目,所以我能夠找出它當時包含的內容。如果你可以讓應用程序在本地運行,你可以附加WinDBG,並暫停程序並自己檢查LOH。 – 2012-03-29 15:23:53

+1

其實,根據我的研究,LOH從來沒有被壓縮過。所以是的,如果我需要它,我可以稍後使用它(我不這樣做),但它只是掛在身邊,而且從我的研究中再次發佈,不會發布到操作系統。 https://connect.microsoft.com/VisualStudio/feedback/details/521147/large-object-heap-fragmentation-causes-outofmemoryexception – 2012-03-29 15:35:48

1

實驗結束後,我可以通知GC什麼時候從一代中刪除東西,但是當它將它放在那裏時不會。由於LOH不是特定於代的,並且沒有特定的事件可以訪問LOH插入的通知,所以我可以提供的唯一選擇是調試應用程序,執行崩潰轉儲 - 或者 - 在本地運行它,並且 - 使用WinDBG。這裏是你如何能做到這一點:

  1. 下載Windows SDK 7
  2. 複製%Microsoft_NET_Framework%\ sos.dll到WinDbg的目錄
  3. 在WinDbg中點擊文件 - >打開可執行文件 - >點到您的可執行
  4. 在底部型g(圍棋)
  5. 監視內存,當你要分析它,去到的WinDBG命令欄 - >調試菜單 - >打破
  6. 類型load .sos - 加載。 ñ ET擴展
  7. 類型!dumpheap -min 85000 - 這將列出大的物體,應駐留在LOH
  Address    MT  Size 
0000000012a17048 000007fee7ae6ae8 400032  
0000000012a78b00 000007fee7ae6ae8 400032  
0000000012ada5b8 000007fee7ae6ae8 400032  
0000000012b3c070 000007fee7ae6ae8 400032  
0000000012b9db28 000007fee7ae6ae8 400032 

接下來,我們需要通過每一項,並找出什麼在其中。

  1. 複印的第一列(對象地址)到剪貼板
  2. 類型!do <paste from clipboard>
  3. 這將列出的對象,它的類型和大小的內容。
CLR Version: 4.0.30319.261 
SOS Version: 4.0.30319.239 
Name:  System.String 
MethodTable: 000007fee7ae6ae8 
EEClass:  000007fee766ed68 
Size:  400026(0x61a9a) bytes 
File:  C:\Windows\Microsoft.Net\assembly\GAC_64\mscorlib\v4.0_4.0.0.0__b77a5c561934e089\mscorlib.dll 
String:  8470737076787475867884758166807183888774746571677189.. 
Fields: 
       MT Field Offset     Type VT  Attr   Value Name 
000007fee7aec9d0 4000103  8   System.Int32 1 instance   200000 m_stringLength 
000007fee7aeb510 4000104  c   System.Char 1 instance    38 m_firstChar 
000007fee7ae6ae8 4000105  10  System.String 0 shared   static Empty 
           >> Domain:Value 000000000055fe50:0000000002a11420 << 

而且你要尋找的線路有:

Size:  400026(0x61a9a) bytes 
String:  8470737076787475867884758166807183888774746571677189.. 
  1. 這樣做對每個對象

(但是,我假設它將是一個字符串,因此請檢查'Name'屬性以確保。它可能是一個數組。)