實作 OutOfMemory
通過代碼實作 OutOfMemory
來嘗試寫一個發生 OutOfMemoryException 的代碼吧,開啟煞筆代碼第三篇 —— OutofMemory
OutOfMemory 顧名思義就是記憶體不足,在 .NET 中當記憶體不足的時候就會抛出 OutOfMemoryException 的異常。
想要觸發 OutOfMemoryException 就要滿足記憶體不足的條件,在 .NET Framework 中可能就隻能一直配置設定記憶體直到記憶體不足,再沒有足夠的記憶體可以配置設定了,在 .NET Core 3.x 版本以後,微軟引入了一些 GC 的配置,我們可以通過這些配置來指定最大的 GC 記憶體,這樣我們就可以實作觸發 OutOfMemoryException 而不影響其他應用程式正常運作的目标了。在 .NET 5 中我們又可以更進一步更精細的控制 GC 使用的記憶體了,在 .NET 5 中我們可以針對每個堆(SOH/LOH/POH)來設定記憶體限制。
我們測試的示例使用限制 GC 堆大小 (Heap Limit) 的方式來限制應用程式的記憶體占用以免影響到别的應用程式正常運作(該配置隻針對 64 位電腦有效,現在的電腦應該大多都是64位吧)。
配置的方式有兩種,一種是通過環境變量來配置,一種是通過 runtime.config.json 來配置
通過環境變量配置 COMPlus_GCHeapHardLimit 為要配置的記憶體大小,需要注意的是通過環境變量配置的時候指定的值需要是十六進制的值,通過 runtimeconfig.json 配置的時候是直接用十進制的數值
因為我們隻是想簡單的測試一下,不能影響别的應用程式,而且不能在代碼裡配置目前程序的環境變量,因為程序啟動的時候 GC 的配置就已經加載好了,在代碼裡配置目前程序的環境變量來改變 GC 配置是不會生效的,是以我們選擇配置 runtimeconfig.json 來測試,在項目的 bin 目錄下可以找到 runtimeconfig.json 檔案,我們修改這一個檔案即可(使用 runtimeconfig.json 的時候需要注意先生成一下,然後再更新 runtimeconfig.json 檔案)
測試配置如下,配置的 GC 堆的最大值是 1M(配置的不能太小,太小的話 CoreCLR 可能都會啟動失敗進而導緻程式無法正常運作):
測試代碼如下:
測試輸出如下:

上面的測試代碼使用的 byte 數組的長度是 85000 的原因是,當要配置設定的對象大于等于 85k(85000)時會直接配置設定到大對象堆中,正好可以測試一下。
我們使用微軟的 dotnet dump 診斷工具來測試一下
第一次 dump 是在 list 對象建立之前進行的,第二次 dump 是發生 OutOfMemory 之後的
從上面的 dump 結果可以看的出來,byte 數組的對象确實是配置設定在大對象堆(LOH)上的,幾乎所有的記憶體配置設定都在大對象堆中,有一些小對象從0 代升到了 1代。
上面的測試代碼使用的 byte 數組的長度是 85000 ,你測試的時候也可以使用更大的值,或者直接使用 int.MaxValue
在前面的 StackOverflow 文章中,有網友評論說,他們之前遇到的一個 StackOverflow 示例常常伴随着 OutOfMemory ,遞歸和這種方式有點類似,都是要一直建立新的對象,配置設定新的記憶體。
除此之外,還有哪些更簡單的方式嗎?歡迎補充