Out of Memory, 電腦RAM老是莫名的不夠用?

最近電腦老是莫名的OOM (Out of Memory)。明明就安裝了32GB的RAM,卻老是說不夠用。把虛擬記憶體加大了數倍,甚至拿另一個RAID M.2 SSD 來當成VM。改動Virtual Memory的設定 ( pagefile.sys、hiberfil.sys、swapfile.sys )都沒用,動不動還是說Memory不夠用。

OOM 的時候電腦會怎麼樣?

老實說,OOM的時候根本狀況百出。不要以為每個程式都有做OOM測試或是保護。除了程式各種崩潰外,最嚴重的莫過於顯示驅動程式根本動不了,如果你不能開個背景遠端DEBUG,那麼只能按下RESET鈕來重啟電腦。其中最讓人頭痛的莫過於玩遊戲的時候,莫名的延遲甚至於程式崩會。 自從換了4K的螢幕後,這個問題尤其明顯。

像我這種從不關機的,OOM的問題根本是個夢靨。常常電腦跑不到一個禮拜就要重啟或是自行崩潰。常常疑神疑鬼想說電腦是不是種了木馬,或是病毒。 但是花了很多時間更新軟體、掃毒、掃木馬加上各種清除,還是沒法根治。

原來還是微軟的鍋(問題)?

這週末,立誓一定要解決這個問題。終於,花了一整天的時間, 找到了。。。一個不是解法的解法,改善了這個問題。

工作管理員是大家常常用來檢視系統運作狀況的主要工具。關於記憶體這個欄位。明明顯示還有近50%的實體記憶體能夠使用,卻出現各種記憶體短缺的錯誤。利用Poolmon這個DDK的工具,可以觀察與監視記憶體POOL的使用狀態。

回頭來檢視記憶體狀態的一個奇特現象。”已快取” 這個欄位的記憶體越堆積越來越高。以現今主流的作業系統來說,快取或是Standby都會被視為可用記憶體。這些記憶體是為了讓平常空閒尚未使用,暫時性的拿來暫存以便加快系統效能。並且,在你真正需要時候轉換成可用記憶體。

既然我們發現Standby是個問題。我們就能進一步查看更仔細的問題。再來利用資源管理器看看 “待命” 的狀態。果不其然,這待命常常能增長到12–15GB。但是,系統顯示可用記憶體的大小時,卻會包含這個待命的大小。

顯示可用實體記憶體 = 真正可用實體記憶體(Unused) + 待命

最後,我們用SysInternals這個大神工具中的 RAMMap 來檢視更實際的記憶體使用狀況。終於發現病灶。這個期待 “待命” 記憶體轉成可用的機制,在Windows 10竟然不能正常作用。最終導致所有的記憶都變成了待命狀態。這就好比Memory Leak了,卻完全不能回收再使用。於是乎,越來越少的真正可用記憶體可提供使用。

這個問題真的是很糟糕。記憶體管理上的重大問題。但是這個問題似乎不是在每一台電腦都發生。目前真正的原因尚不清楚,Windows 10 1809 這個最新版本還是有這個問題的。我的觀察或許跟大量使用檔案IO等等好像有所關係。難道因為我養了一堆動物有關嗎?

解決方案!

其實知道了問題,就好解決了。只是這個是微軟作業系統的問題。目前也沒找到甚麼正解。一個簡易的解決方案就是,哪邊跌倒,哪邊站起來。

Empty Standby List
利用這個小工具來強迫系統清空待命的記憶體。也就是等同手動自己把待命轉為可用。

那麼何時該使用呢?說真的因為沒法有效察覺這個問題。如果寫個工具一值存取系統記憶體的使用情況,似乎不是一個聰明的作法。畢竟,待命本來就有其真正扮演的工作要做,不間斷地轉換待命為可用並不是一個理想的解決方案。但是等到資源不足再觸發,代價也不低。也許最簡單的方法還是固定某個時間去週期性的執行這個動作。

簡單效能分析:

每60分鐘執行,”待命” 最大會累積到 5GB左右。
每30分鐘執行,”待命” 最大會累積到 2GB左右。

老實說,這真的不是甚麼好方法。而且也不是沒有副作用。但是真的好過發生OOM。因為一旦OOM發生,整個系統都變得超級不可靠的。還不如直接重新啟動電腦。

至於如何排程:

跟Linux/Unix/Mac的Cron一樣,Windows也有自己的排程,以前at指令現在已經沒人用了,用Windows排程工具。然後用排程工具,週期性的執行這個程式來暫時解決這個惱人的問題吧!

補充說明:

更新到1903後,這個問題似乎獲得了改善。另外,補充一個PoolmonX工具。是個GUI版本的Poolmon。這樣連WinDbg都不用安裝了。

PoolmonX

PoolmonX

另一種RAM莫名的消失方式,但是又難以追查:

我的電腦安裝64GB的RAM,但是卻還是莫名的RAM用光了。這個問題也不是前面提到那種Standby無法轉成可用記憶體的問題。這個問題可能跟驅動程式或是資源遺漏有關。這時候,我們又得用RamMap這個程式來觀察問題。

RamMap Indicated Shareable Memory Leak

如圖,你可以觀察到大量的Shareable而且處於Modified狀態。實際上,Active卻很少。這問題有時候是很難以察覺的。因為這些資源耗損無法表現在Process的Private Bytes上。你很難觀察到哪個應用或是那個驅動導致這個問題。可是問題明明就出現,而且你的可用RAM卻越來越少。這邊還要特別說明一下,有時候你固然有可用RAM,但是系統上有個Commited Limit這個限制,這意味著你還有有足夠的記憶體分頁能提供配置,你才能真正的使用這些資源。但是通常這時候,因為各種資源流失,你帳面上固然還有資源,卻已經無法使用。

Process Hacker

Process Hacker System Information

Process Hacker 是個與 Process Explorer 類似的工具。但是提供了一些不錯的觀察工具。

為了追查我們剛剛說的Shareable Memory Leak,我們得回頭用我們系統內建的工作管理員。

Task Manager Sort By Handles

在詳細的欄位中,用控制代碼Handle來排序,你可以發現大量的控制代碼使用。這些不合理的使用,很明確地告訴我們這個應用本身是有很大問題的。所以我們嘗試關閉他,然後在到RamMap中觀察變化。

關閉前,

關閉後,

我們可以注意到,當問題應用被關閉後,大量Modified已經消失。由4.3GB轉變成1.4GB。約有3GB的RAM被重新轉為待命或是可用的記憶體。

補充說明2: (2020.04.20 更新)

另一種簡易的檢視APP是否濫用Memory的方式:
打開工作管理員,選取認可大小。

工作管理員,選取 "認可大小" 欄位

認可(Commit)記憶體 = 實體記憶體 + 虛擬記憶體

簡單的說就是可使用的記憶體空間大小。檢視認可大小是一個簡易卻又能整體的看出該應用程序分配了多少記憶體配額。所以一個消耗據大記憶體配額的程序就是一個值得觀察的對象。

工具

最後,我們整理一下我們用到的工具程式:

Poolmon: 這個非一個獨立工具,可以在Windows SDK中下載到。PoolmonX: Poolmon的UI版本。使用他就不用下載SDK工具了。
RAMMap:這個就是一定要擁有的工具程式了!
ProcessExplorer:高級版本的工作管理員。
ProcessHacker: 另一個絕對必須入手的工具。
Empty Standby List: 可以快速把StandBy轉回可用記憶體的工具。

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store