共用方式為


針對 system.OutOfMemoryException) 中 system.OutOfMemoryException (的記憶體不足問題進行疑難解答 ASP.NET

本文可協助您針對 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 位進程可以處理 4 GB 的使用者模式記憶體,而在 64 位操作系統上執行的 64 位進程可以處理 8TB 的使用者模式記憶體,因此 64 位操作系統上的 OOM 不太可能。 您可以在 64 位作業系統上執行的 32 位進程中體驗 OOM,但通常要等到進程使用接近 3 GB 的私人位元組才會發生。

您可能會看到 OOM 條件的原因有兩個。

  1. 您的程式在32位環境中使用許多記憶體 (通常超過800 MB。)
  2. 虛擬位址空間分散,可降低大型連續配置成功的可能性。

由於 1 和 2 的組合,您也可以看到 OOM 條件。 如需詳細資訊,請參閱針對 ASP.NET 中的 System.OutOfMemoryExceptions 進行疑難解答

發生 OOM 時,您可能會注意到下列一或多個徵兆:

在判斷 OOM 狀況的原因時,您實際上是在判斷高記憶體狀況或地址空間分散的原因。 雖然我們無法記錄這些情況的所有可能原因,但我們仍會定期看到一些常見的原因。

下列資訊概述 OOM 條件的常見原因,以及解決每個原因的解決方法。

字串連鎖

使用 .NET Framework) 寫入的應用程式 (受控應用程式中的字串是不可變的。 將新值指派給字串時,會建立現有字串的複本。 而新的值會指派給新的字串。 通常不會造成任何問題。 但是,當串連大量字串時,最終會造成比開發人員可能發現更多的字串配置。 而且可能會導致記憶體成長和 OOM 條件。

若要避免因為字串串連而導致 OOM,請確定您使用的 StringBuilder 是 類別。 如需詳細資訊,請 參閱如何改善Visual C# 中的字串串連效能

受控堆積中的片段

受控應用程式中的垃圾收集行程 (GC) 壓縮堆積,以減少片段的數量。 不過,您可以在受控應用程式中釘選物件。 在堆積壓縮期間無法移動釘選的物件。 這樣做會變更物件所在的位址。 如果應用程式釘選大量物件,而且/或是長時間釘選物件,則可能會導致Managed堆積中的片段。 這可能會導致 GC 更頻繁地增加 Managed 堆積,並導致 OOM 條件。

自 .NET Framework 1.0 以來,我們一直致力於將 OOM 條件降至最低。 每個版本都已進行增量改善。 不過,如果您需要釘選物件,仍有一些您可以實作的設計模式會有所説明。

虛擬位址 (VA) 空間中的片段

每個進程都有一定數量的記憶體配置給它,而該記憶體代表進程的 VA 空間。 如果 VA 空間變得分散,則會增加 GC 無法取得大量連續記憶體區塊來增加受控堆積的可能性。 而且可能會導致 OOM 條件。

VA 空間中的片段通常是由下列一或多個案例所造成:

  • 將相同的元件載入多個應用程式域。

    如果您需要在相同應用程式集區中執行的多個應用程式中使用元件,請將元件強名稱化,並將其安裝到 GAC 中。 如此一來,您就可以確保元件只會載入進程一次。

  • 在生產環境執行應用程式,並將項目的偵錯屬性 <compilation> 設定為 true

  • 在 eXtensible Style sheet Language 中使用腳本 (XSL) 轉換或建立 XmlSerializers

    在這裡情況下,可延伸樣式表語言轉換所造成的動態元件 (XSLT) 文稿或 XmlSerializers

傳回大型數據集

使用來自資料庫或其他數據源的數據時,請務必限制傳回的數據量。 例如,快取會傳回整個資料庫數據表的查詢結果,以避免在需要時從資料庫擷取部分數據的成本不是不錯的方法。 這樣做很容易導致高記憶體,並導致 OOM 條件。 允許使用者啟動類似的查詢是建立高記憶體情況的另一個常見方式。 例如,傳回公司中的所有員工,或是姓氏開頭為字母 S 的所有德州客戶。

一律限制可從資料庫傳回的數據量。 不允許這類 SELECT * FROM. . . 查詢,因為您接著無法控制頁面中顯示的數據量。

確保您不會在UI元素中顯示大型數據結果也同樣重要,例如 GridView 控制件。 除了傳回數據所需的記憶體之外,您也會在字串和轉譯結果所需的 UI 元素中取用大量數據。 藉由實作分頁和驗證輸入,讓大型數據集不會傳回,您可以避免這個問題。

在已啟用追蹤的生產環境中執行

ASP.NET 追蹤是針對應用程式進行疑難解答的強大功能。 但是,它絕對不應該保留在生產環境中。 ASP.NET 追蹤會使用例如 的數據結構 DataTables 來儲存追蹤資訊,而且在一段時間之後,它們可能會導致高記憶體狀況而導致 OOM。

應該在生產環境中停用追蹤。 若要這麼做, enabled 您可以在web.config檔案中將 項目的 屬性 <trace> 設定為 false 。 使用 啟用 <deploy retail="true" /> 零售部署也會停用應用程式中的追蹤。

洩漏原生資源

許多受控資源也會使用原生資源。 因為 GC 不會清除原生資源,所以開發人員負責實作和呼叫 Dispose 方法來清除原生資源。 如果您使用實作 介面的 IDisposable 類型,但未呼叫 Dispose 方法,則有洩漏原生資源並造成 OOM 條件的風險。

這些對象應該實作 介面, iDisposable 而且當您不再需要這些物件時,應該在這些物件上呼叫 Dispose 方法。