实现 OutOfMemory
通過(guò)代碼實(shí)現(xiàn) OutOfMemory
Intro
來(lái)嘗試寫(xiě)一個(gè)發(fā)生 OutOfMemoryException 的代碼吧,開(kāi)啟煞筆代碼第三篇 —— OutofMemory
OutOfMemory
OutOfMemory 顧名思義就是內(nèi)存不足,在 .NET 中當(dāng)內(nèi)存不足的時(shí)候就會(huì)拋出 OutOfMemoryException 的異常。
想要觸發(fā) OutOfMemoryException 就要滿足內(nèi)存不足的條件,在 .NET Framework 中可能就只能一直分配內(nèi)存直到內(nèi)存不足,再?zèng)]有足夠的內(nèi)存可以分配了,在 .NET Core 3.x 版本以后,微軟引入了一些 GC 的配置,我們可以通過(guò)這些配置來(lái)指定最大的 GC 內(nèi)存,這樣我們就可以實(shí)現(xiàn)觸發(fā) OutOfMemoryException 而不影響其他應(yīng)用程序正常運(yùn)行的目標(biāo)了。在 .NET 5 中我們又可以更進(jìn)一步更精細(xì)的控制 GC 使用的內(nèi)存了,在 .NET 5 中我們可以針對(duì)每個(gè)堆(SOH/LOH/POH)來(lái)設(shè)置內(nèi)存限制。
GC 堆內(nèi)存限制配置
我們測(cè)試的示例使用限制 GC 堆大小 (Heap Limit) 的方式來(lái)限制應(yīng)用程序的內(nèi)存占用以免影響到別的應(yīng)用程序正常運(yùn)行(該配置只針對(duì) 64 位電腦有效,現(xiàn)在的電腦應(yīng)該大多都是64位吧)。
配置的方式有兩種,一種是通過(guò)環(huán)境變量來(lái)配置,一種是通過(guò) runtime.config.json 來(lái)配置
通過(guò)環(huán)境變量配置 COMPlus_GCHeapHardLimit 為要配置的內(nèi)存大小,需要注意的是通過(guò)環(huán)境變量配置的時(shí)候指定的值需要是十六進(jìn)制的值,通過(guò) runtimeconfig.json 配置的時(shí)候是直接用十進(jìn)制的數(shù)值
因?yàn)槲覀冎皇窍牒?jiǎn)單的測(cè)試一下,不能影響別的應(yīng)用程序,而且不能在代碼里配置當(dāng)前進(jìn)程的環(huán)境變量,因?yàn)檫M(jìn)程啟動(dòng)的時(shí)候 GC 的配置就已經(jīng)加載好了,在代碼里配置當(dāng)前進(jìn)程的環(huán)境變量來(lái)改變 GC 配置是不會(huì)生效的,所以我們選擇配置 runtimeconfig.json 來(lái)測(cè)試,在項(xiàng)目的 bin 目錄下可以找到 runtimeconfig.json 文件,我們修改這一個(gè)文件即可(使用 runtimeconfig.json 的時(shí)候需要注意先生成一下,然后再更新 runtimeconfig.json 文件)
測(cè)試配置如下,配置的 GC 堆的最大值是 1M(配置的不能太小,太小的話 CoreCLR 可能都會(huì)啟動(dòng)失敗從而導(dǎo)致程序無(wú)法正常運(yùn)行):
{"runtimeOptions":?{"tfm":?"netcoreapp3.1","framework":?{"name":?"Microsoft.NETCore.App","version":?"3.1.0"},"configProperties":?{"System.GC.HeapHardLimit":?1048576}} }測(cè)試代碼
測(cè)試代碼如下:
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();測(cè)試輸出如下:
上面的測(cè)試代碼使用的 byte 數(shù)組的長(zhǎng)度是 85000 的原因是,當(dāng)要分配的對(duì)象大于等于 85k(85000)時(shí)會(huì)直接分配到大對(duì)象堆中,正好可以測(cè)試一下。
我們使用微軟的 dotnet dump 診斷工具來(lái)測(cè)試一下
第一次 dump 是在 list 對(duì)象創(chuàng)建之前進(jìn)行的,第二次 dump 是發(fā)生 OutOfMemory 之后的
從上面的 dump 結(jié)果可以看的出來(lái),byte 數(shù)組的對(duì)象確實(shí)是分配在大對(duì)象堆(LOH)上的,幾乎所有的內(nèi)存分配都在大對(duì)象堆中,有一些小對(duì)象從0 代升到了 1代。
More
上面的測(cè)試代碼使用的 byte 數(shù)組的長(zhǎng)度是 85000 ,你測(cè)試的時(shí)候也可以使用更大的值,或者直接使用 int.MaxValue
在前面的 StackOverflow 文章中,有網(wǎng)友評(píng)論說(shuō),他們之前遇到的一個(gè) StackOverflow 示例常常伴隨著 OutOfMemory ,遞歸和這種方式有點(diǎn)類(lèi)似,都是要一直創(chuàng)建新的對(duì)象,分配新的內(nèi)存。
除此之外,還有哪些更簡(jiǎn)單的方式嗎?歡迎補(bǔ)充
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
總結(jié)
以上是生活随笔為你收集整理的实现 OutOfMemory的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 回顾 | 使用Visual Studio
- 下一篇: 福利 | 全网疯传免费领,一整套算法课程