本文可協助您針對 ASP.NET 中的記憶體不足錯誤進行疑難解答。
原始產品版本: ASP.NET
原始 KB 編號: 2020006
徵兆
我們在 Microsoft客戶支援服務中看到的最常見問題之一是 OutOfMemoryException 案例。 因此,我們將資源集合放在一起,以協助疑難解答和識別記憶體問題的原因。
在討論疑難解答 OutOfMemoryException的詳細數據之前,請務必瞭解造成此問題的原因。 與許多開發人員所相信的相反,所安裝的 RAM 數量不會影響的可能性 OutOfMemoryException。 32 位作系統可以處理 4 GB 的虛擬位址空間,無論方塊中安裝的實體記憶體數量為何。 其中,2 GB 會保留給作系統(內核模式記憶體),並將 2 GB 配置給使用者模式進程。 配置給內核模式記憶體的 2 GB 會在所有進程之間共用,但每個進程都會取得自己的 2 GB 使用者模式地址空間。 全都假設您未在啟用 參數時 /3gb 執行。
當應用程式需要使用記憶體時,它會保留虛擬位址空間的區塊,然後認可該區塊的記憶體。 這正是 .NET Framework 垃圾收集行程 (GC) 在需要記憶體來成長受控堆積時所執行的方式。 當 GC 需要小型物件堆積的新區段時(物件小於 85 KB 所在的位置),它會配置 64 MB。 當它需要大型物件堆積的新區段時,它會配置 16 MB。 這些大型配置必須滿足於進程必須處理之 2 GB 位址空間的連續區塊。 如果作系統無法滿足 GC 對連續記憶體區塊的要求, System.OutOfMemoryException 就會發生 (OOM) 。
注意
在 64 位作系統上執行的 32 位進程可以處理 4GB 的使用者模式記憶體,而 64 位作系統上執行的 64 位進程可以處理 8TB 的使用者模式記憶體,因此 OOM 在 64 位作系統上不太可能。 在 64 位作系統上執行的 32 位進程中,可能會發生 OOM,但通常不會發生,直到進程使用接近 3 GB 的私人位元組為止。
您可能會看到 OOM 條件的原因有兩個。
- 您的進程使用大量記憶體(通常在32位環境中超過800 MB。
- 虛擬位址空間會分散,以減少大型連續配置成功的可能性。
也可能會因為 1 和 2 的組合而看到 OOM 條件。 如需詳細資訊,請參閱 針對 ASP.NET 中的 System.OutOfMemoryExceptions 進行疑難解答。
發生 OOM 時,您可能會注意到下列一或多個徵兆:
您的應用程式當機。 如需詳細資訊,請參閱誰是 OutOfMemory 傢伙,為什麼當我留下大量的記憶時,他會讓我的進程當機?
您的應用程式可能會遇到高記憶體,如任務管理員或 效能監視器 所示。
要求可能需要很長的時間才能處理。
在 網際網路資訊服務 (IIS) 7 上,您可以使用在 IIS 7 中使用追蹤對失敗的要求進行疑難解答,針對長時間執行的要求進行疑難解答。
使用者可能會因為 OOM 而回報應用程式中的錯誤訊息。
在判斷 OOM 條件的原因時,您實際上正努力判斷高記憶體狀況或分散地址空間的原因。 雖然我們無法記錄這些情況的所有可能原因,但我們經常看到一些常見原因。
下列資訊概述 OOM 條件的常見原因,以及解決每個原因的解決方式。
字串串連
受控應用程式中的字串(使用 .NET Framework 撰寫的應用程式)是不可變的。 將新值指派給字串時,會建立現有字串的複本。 新的值會指派給新的字串。 這通常不會造成任何問題。 但是,當大量字串串連時,它最終會導致比開發人員可能意識到的更多字串配置。 這可能會導致記憶體成長和 OOM 條件。
若要避免因字串串連而造成 OOM,請確定您使用 StringBuilder 類別。 如需詳細資訊,請參閱 如何在Visual C# 中改善字串串連效能。
Managed 堆積中的片段
受控應用程式中的垃圾收集行程 (GC) 會壓縮堆積,以減少片段量。 不過,可以釘選受控應用程式中的物件。 固定的物件無法在堆積壓縮期間移動。 這樣做會變更物件所在的位址。 如果應用程式釘選大量物件,且/或釘選對象很長一段時間,可能會導致 Managed 堆積中的片段。 這可能會導致 GC 更頻繁地成長受控堆積,並導致 OOM 條件。
由於 .NET Framework 1.0 之後的釘選,我們已努力將 OOM 條件降至最低。 每個版本都已進行累加式改善。 不過,如果您需要釘選物件,您仍然可以實作設計模式,這會很有説明。
虛擬位址 (VA) 空間中的片段
每個進程都有一定數量的記憶體配置給它,而該記憶體代表進程的 VA 空間。 如果 VA 空間變得分散,就會增加 GC 無法取得大量連續記憶體區塊以增加受控堆積的可能性。 這可能會導致 OOM 條件。
VA 空間中的片段通常是由下列一或多個案例所造成:
將相同的元件載入多個應用程式域。
如果您需要在相同應用程式集區中執行的多個應用程式中使用元件,請強名稱元件並將其安裝到 GAC。 如此一來,您可確保元件只會載入進程一次。
在生產環境執行應用程式,並將項目的偵錯屬性
<compilation>設定為true。- 項目的偵錯屬性
<compilation>應該是false在生產環境中時。 - 您可以使用組
<deploy retail="true" />態來確保產品中一律停用偵錯。 如需詳細資訊,請參閱 deployment Element (ASP.NET Settings Schema)。
- 項目的偵錯屬性
在 eXtensible 樣式表單語言 (XSL) 轉換或建立
XmlSerializers內使用腳本。在此情況下,由可延伸樣式表單語言轉換 (XSLT) 腳本或
XmlSerializers所造成的動態元件。
傳回大型數據集
使用來自資料庫或其他數據源的數據時,請務必限制傳回的數據量。 例如,快取會傳回整個資料庫數據表的查詢結果,以避免在需要時從資料庫擷取部分數據的成本不是很好的方法。 這樣做很容易造成高記憶體,並導致 OOM 條件。 允許使用者啟動類似的查詢,是建立高記憶體狀況的另一個常見方式。 例如,以字母 S 開頭的姓氏傳回公司或德克薩斯州所有客戶中的所有員工。
一律限制可從資料庫傳回的數據量。 不允許查詢,例如 SELECT * FROM. . . ,因為您接著無法控制頁面中顯示的數據量。
請務必確保不會在UI元素中顯示大型數據,例如 GridView 控件。 除了傳回數據所需的記憶體之外,您也會在字串和UI元素中取用大量數據,這些元素需要轉譯結果。 藉由實作分頁和驗證輸入,讓不會傳回大量數據集,您可以避免這個問題。
在已啟用追蹤的生產環境中執行
ASP.NET 追蹤是針對應用程式進行疑難解答的強大功能。 但它絕不應該留在生產環境中。 ASP.NET 追蹤會使用數據結構,例如 DataTables 儲存追蹤資訊,而且隨著時間推移,可能會導致 OOM 的高記憶體狀況。
應該在生產環境中停用追蹤。 您可以將 元素的 屬性設定enabled為 web.config 檔案中的 false,即可<trace>這麼做。 使用 <deploy retail="true" /> 啟用零售部署也會停用應用程式中的追蹤。
流失原生資源
許多受控資源也會使用原生資源。 由於 GC 不會清除原生資源,因此開發人員會負責實作和呼叫 Dispose 方法來清除原生資源。 如果您使用實作 介面的類型 IDisposable ,而且您未呼叫 Dispose 方法,則可能會洩漏原生資源並造成 OOM 條件。
這些對象應該實作 iDisposable 介面,而且當您不再需要這些物件時,您應該在這些物件上呼叫 Dispose 方法。