通過代碼實作 OutOfMemory
Intro
來嘗試寫一個發生 OutOfMemoryException 的代碼吧,開啟煞筆代碼第三篇 —— OutofMemory
OutOfMemory
OutOfMemory 顧名思義就是記憶體不足,在 .NET 中當記憶體不足的時候就會拋出 OutOfMemoryException 的例外,
想要觸發 OutOfMemoryException 就要滿足記憶體不足的條件,在 .NET Framework 中可能就只能一直分配記憶體直到記憶體不足,再沒有足夠的記憶體可以分配了,在 .NET Core 3.x 版本以后,微軟引入了一些 GC 的配置,我們可以通過這些配置來指定最大的 GC 記憶體,這樣我們就可以實作觸發 OutOfMemoryException 而不影響其他應用程式正常運行的目標了,在 .NET 5 中我們又可以更進一步更精細的控制 GC 使用的記憶體了,在 .NET 5 中我們可以針對每個堆(SOH/LOH/POH)來設定記憶體限制,
GC 堆記憶體限制配置
我們測驗的示例使用限制 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 可能都會啟動失敗從而導致程式無法正常運行):
{
"runtimeOptions": {
"tfm": "netcoreapp3.1",
"framework": {
"name": "Microsoft.NETCore.App",
"version": "3.1.0"
},
"configProperties": {
"System.GC.HeapHardLimit": 1048576
}
}
}
測驗代碼
測驗代碼如下:
Console.ReadLine();
var bytes = GC.GetTotalAllocatedBytes();
Console.WriteLine($"AllocatedBytes: { bytes } bytes");
var list = new List<byte[]>();
try
{
while (true)
{
list.Add(new byte[85000]);
}
}
catch (OutOfMemoryException)
{
Console.WriteLine(nameof(OutOfMemoryException));
Console.WriteLine(list.Count);
bytes = GC.GetTotalAllocatedBytes();
Console.WriteLine($"AllocatedBytes: { bytes } bytes");
}
Console.ReadLine();
測驗輸出如下:

上面的測驗代碼使用的 byte 陣列的長度是 85000 的原因是,當要分配的物件大于等于 85k(85000)時會直接分配到大物件堆中,正好可以測驗一下,
我們使用微軟的 dotnet dump 診斷工具來測驗一下

第一次 dump 是在 list 物件創建之前進行的,第二次 dump 是發生 OutOfMemory 之后的
從上面的 dump 結果可以看的出來,byte 陣列的物件確實是分配在大物件堆(LOH)上的,幾乎所有的記憶體分配都在大物件堆中,有一些小物件從0 代升到了 1代,
More
上面的測驗代碼使用的 byte 陣列的長度是 85000 ,你測驗的時候也可以使用更大的值,或者直接使用 int.MaxValue
在前面的 StackOverflow 文章中,有網友評論說,他們之前遇到的一個 StackOverflow 示例常常伴隨著 OutOfMemory ,遞回和這種方式有點類似,都是要一直創建新的物件,分配新的記憶體,
除此之外,還有哪些更簡單的方式嗎?歡迎補充
References
- https://docs.microsoft.com/en-us/dotnet/core/run-time-config/garbage-collector#heap-limit
- https://github.com/WeihanLi/SamplesInPractice/blob/master/StupidSamples/FullMemorySample.cs
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/192002.html
標籤:.NET Core
