在運維工作中,經常能接到客戶的反饋這個:引發類型為“System.OutOfMemoryException”的異常。客戶反饋物理內存都還有富余,怎么報內存不足的錯誤呢!
什么時候會引發System.OutOfMemoryException:操作系統無法滿足GC對連續內存塊的請求,則會發生System.OutOfMemoryException
可能原因
1:內存真的不夠了,連虛擬內存都用完了。
2:內存還有,但碎片化嚴重,無法找到合適的連續內存塊。
一些常見的原因
1、系統里緩存了大量的數據,沒有及時釋放。應該控制緩存的數據大小,緩存失效的時間。
2、操作的大數據的文件或者DataTable。應該分塊讀取。
3、本地資源泄漏。
解決方案:
3.1 應該及時釋放本地資源
3.2 使用IDisposable模式
4、大量的對象被固定,不能被壓縮移動,導致內存產生大量碎片。
解決方案:
4.1 如果固定對象大約在同一時間分配,則每兩個對象之間的碎片更小
4.2 優先初始化,因為較舊的對象位於堆的底部,但大多數可用空間都是在堆頂部生成的。
4.3 固定對象的時間越短,GC就越容易壓縮堆
測試環境:物理內存8G,64位操作系統
示例一、物理內存,虛擬內存都被消耗完,拋出System.OutOfMemoryException的異常
using System; using System.Collections.ObjectModel; namespace OutOfMemoryExample { class Program { static List<byte[]> _root = new List<byte[]>(); static void Main(string[] args) { try { int i = 1; while (true) { var smallObject = new byte[64 * 1024];//小於85000byte的對象存在小對象堆(SOH – small object heap) Console.WriteLine(string.Format("{0}K", i * 64)); _root.Add(smallObject);//用靜態List對象不斷新增小對象,由於靜態對象不會被回收,最終會發現物理內存和虛擬內存都耗盡,拋出OutOfMemory異常 i++; } } catch (Exception ex) { Console.WriteLine(ex); } Console.Read(); } } } }
示例二、物理內存還有4G,虛擬內存都被消耗完(22G),
大對象(大於85000byte)存儲到大對象堆,大對象堆屬於第二代堆,垃圾回收時不會壓縮內存,分配一個大對象時,優先嘗試在大對象堆的尾部進行分配,如果尾部空間不足,就會嘗試向操作系統請求更多的內存空間,
前面都失敗失敗時,才會重新搜索之前無效對象留下的內存空隙。如果沒有找到連續的內存塊,拋出System.OutOfMemoryException的異常。
疑問:為什么物理內存還有,大對象堆為什么向操作系統申請不到內存?
我推測是這些剩余的內存被一些程序給預留了,或者沒有大的內存塊。
using System; using System.Collections.ObjectModel; namespace OutOfMemoryExample { class Program { static List<byte[]> _root = new List<byte[]>(); static void Main(string[] args) { try { for(int i=1;i<500000;i++) { var largeObject = new byte[100 * 1024];//大於85000byte的對象存在一個大對象堆(LOH –large object heap) Console.WriteLine(string.Format("第{0}次:{1}K",i, i * 100)); _root.Add(largeObject);//用靜態List對象不斷新增對象,由於靜態對象不會被回收,最終會發現虛擬內存都耗盡,拋出OutOfMemory異常!但物理內存還有4G i++; } } catch (Exception ex) { Console.WriteLine(ex); } Console.Read(); } } }