2025年4月14日 星期一

[研究]ASP.NET, 攔截 System.OutOfMemoryException

[研究]ASP.NET, 攔截 System.OutOfMemoryException

2025-04-14

環境:Visual Studio 2022 + ASP.NET + WebForm + Web Application + C# + SQL Server 2019 + SQL Server Management Studio (SSMS) 19

********************************************************************************

ASP.NET , Webform, try catch 如何攔截  'System.OutOfMemoryException'  問題 ?

ChatGPT 說:

在 ASP.NET Web Forms 中,System.OutOfMemoryException 是一種 critical error(重大例外),通常表示應用程式的記憶體資源已經用盡,無法再分配更多記憶體。雖然你可以使用 try-catch 嘗試攔截它,但在實務上 這類錯誤常常會導致應用程式崩潰或不穩定,即使有 try-catch 包住也未必有效防範或繼續執行程式。

.aspx.cs 中用 try-catch 包起來的基本範例

try
{
    // 模擬可能導致記憶體爆炸的操作
    byte[] hugeArray = new byte[int.MaxValue]; // 這幾乎會直接爆掉

    // 其他邏輯
}
catch (OutOfMemoryException ex)
{
    // Logging:記錄錯誤
    LogError("Out of memory: " + ex.Message);

    // 顯示友善錯誤頁
    Server.Transfer("OutOfMemoryErrorPage.aspx");
}
catch (Exception ex)
{
    // 一般例外處理
    LogError("General error: " + ex.Message);
}


為什麼攔不住?

  • OutOfMemoryException 發生時,系統可能已經無法提供足夠的資源來正常執行 catch 區塊內的程式碼(例如 logging、轉頁等)。
  • 某些情況下 ASP.NET 執行緒甚至會被終止。

****************************************

更推薦的方式:在 Global.asax的 Application_Error中處理

.aspx.cs 中不用 try-catch 包起來

    byte[] hugeArray = new byte[int.MaxValue]; // 這幾乎會直接爆掉

在 Global.asax的 Application_Error中處理

protected void Application_Error(object sender, EventArgs e)
{
    Exception ex = Server.GetLastError();

    if (ex is OutOfMemoryException)
    {
        // 記錄錯誤
        LogError("應用程式記憶體耗盡: " + ex.Message);

        // 清除錯誤並導向錯誤頁
        Server.ClearError();
        Response.Redirect("~/OutOfMemoryErrorPage.aspx");
    }
}

註:實際測試,如果 Default.aspx.cs 中已經有 try ... catch 攔截,Global.asax 中的 Application_Error 中就攔截不到了。

註:實際測試,如果 Default.aspx.cs 中沒有 try ... catch 攔截,Global.asax 中的 Application_Error 中依然攔截不到,下圖。



****************************************

其他預防建議:

  • 避免大資料操作(如載入大型檔案到記憶體、產生大型報表等)。
  • 釋放不用的資源

using (MemoryStream ms = new MemoryStream())
{
    // 使用完畢會自動釋放記憶體
}

  • 使用 64-bit 應用程式池,讓 ASP.NET 可使用更多記憶體(預設 IIS 常是 32-bit)。
  • 使用 GC.Collect() 非常謹慎! 通常不建議手動叫用垃圾回收,除非你確定真的需要。
(下圖)Windows Server 2019的 IIS 10.0 畫面


(下圖)「啟用32位元應用程式」預設是 True


(完)

相關

沒有留言:

張貼留言