聊一聊 .NET高階除錯 中的一些記憶體術語

一線碼農發表於2023-12-04

一:背景

1. 講故事

在高階除錯的旅程中,經常會有一些朋友問我什麼是 工作集(記憶體),什麼是 提交大小,什麼是 Virtual Size, 什麼是 Working Set 。。。截圖如下:

既然有很多朋友問,這些用口頭也不怎麼好描述,剛好上午有時間就係統的聊一下吧。

二:記憶體術語解讀

1. Virtual Size 是什麼

可能有些朋友知道,記憶體中的虛擬地址被劃分成了三類。

  • Reserved (預定地址)
  • Committed (提交地址)
  • Free (蠻荒地址)

上面的 預定+提交 就是我們的 Virtual Size,即 Virtual Size = Reserved + Committed

當然口說無憑,得要拿出證據,寫一個 x86 的 C# 測試程式碼,參考如下:


        static void Main(string[] args)
        {
            Console.WriteLine("hello world!");
            Console.ReadLine();
        }

將程式跑起來後用 windbg 附加,使用 !address -summary 將計算出的記憶體和 Process Explorer 工具顯示的 Virtual Size 進行對比,截圖如下:

有些較真的朋友可能說:Explorer 顯示出的是 163.300,而 windbg 顯示的是 163.281 ,為什麼還差一點點,其實這是不同工具的統計誤差,僅此而已。

2. Working Set

有些朋友可能知道,一個程式所佔的記憶體最終會在三個地方落地:

  • 實體記憶體條
  • 虛擬記憶體 pagefile
  • 物理檔案 MappedFile

這裡的 Workding Set 特指的就是 實體記憶體條 ,由於 Windows 有 MappedFile 這種檔案對映(記憶體共享)機制,所以實體記憶體條上的記憶體可以進一步劃分為 自己獨佔的 + 大家共享的,可能有些朋友比較蒙,截個圖如下:

有了這張圖的基礎,轉化為專業術語就是:

  • Workding Set = WS Private + WS Shareable

最後我們還是用 Explorer 觀察下剛才的 C# 程式,截圖如下:

3. Private Bytes

剛才我們說到了記憶體最終會落地到三個地方,其中一個地方就是 虛擬記憶體(pagefile),簡而言之它的作用就是給實體記憶體打輔助,這個 pagefile.sys 預設是在 C 盤上,截圖如下:

有了這些基礎,就可以列出一個公式了。

  • Private Bytes = WS Private + Pages Out (pagefile)

上面的 Pages Out 是我定義的換頁記憶體,這個 Private Bytes 指標在分析記憶體洩露的場景下特別有用,它能夠準備的洞察當前程式是否存在大量的 Pages Out(換頁記憶體)

為了方便演示出現了大量的換頁記憶體,寫一個不斷灌資料的例子。


    internal class Program
    {
        static void Main(string[] args)
        {
            var list = new List<string>();

            for (int i = 0; i < 100000000; i++)
            {
                list.Add(string.Join(",", Enumerable.Range(0, 100000)));

                if (i % 10000 == 0) { Console.WriteLine($"i={i}"); }
            }
            Console.WriteLine("成功!");

            Console.ReadLine();
        }
    }

將程式跑起來後,截圖如下:

根據剛才的計算公式:Pages Out = Private Bytes - WS Private ,可以得知大概有 29G 不得不存放在 pagefile 中。

本來想用 wmic pagefile get /value 看一下當前機器的虛擬記憶體佔用,發現有時候不準,我也沒太深究了,輸出如下:


C:\Users\Administrator>wmic pagefile get /value

AllocatedBaseSize=49464
Caption=C:\pagefile.sys
CurrentUsage=1473
Description=C:\pagefile.sys
InstallDate=20230807095038.481750+480
Name=C:\pagefile.sys
PeakUsage=1640
Status=
TempPageFile=FALSE

不過可以看到,這個 pagefile.sys 已經從剛開始的 4.8G 暴漲到 49G 了,其中一大半都被我的程式吞掉了。

4. WS Shared

這個也是很多朋友會問的,WS ShareableWS Shared 到底有什麼區別,從字面意思上看就是:一個可被多個程式共享的記憶體頁集合中,當前已經被共享的記憶體頁集合。

可能這麼說大家有點懵逼,不過沒關係,可以藉助 VMMap 工具觀察。

  1. 開啟一個 ConsoleApp6 程式觀察

從圖中可以看到 Shareable=104k,而 Shared=0k ,這是什麼意思呢? 由於 ConsoleApp6.exe 是檔案對映到記憶體的,佔用了 104k 的實體記憶體,此時沒有其他程式共享這一塊實體記憶體,所以此時為Shared=0,要想把這裡的 Shared 也給填充起來,最簡單的辦法就是開啟多個ConsoleApp6例項。

  1. 開啟多個 ConsoleApp6 程式觀察

接下來反覆點選 ConsoleApp6 生成多個例項,再次使用 VMMap 觀察,截圖如下:

三:總結

我盡最大努力透過多個觀察工具用眼見為實的方式把這幾個記憶體指標系統的說了一下,希望大家對這幾個術語不再迷茫,以後有人問類似問題就可以把這篇丟過去,減輕了你我負擔...

圖片名稱