Windows 記憶體的深層(第 1 回) ── 虛擬位址變成實體 RAM 的瞬間:頁面錯誤從頭到尾

· · Windows, 記憶體管理, VirtualAlloc, 頁面錯誤, VAD, 效能監視

VirtualAlloc 傳入 MEM_COMMIT 時,Commit 會立刻增加。不過 Working Set 不一定會同步增加同樣的量。那麼,你以為已經配置的記憶體在哪裡?

答案是 大多數頁還沒有對應的實體 RAM。Windows 會把實體頁的指派延遲到應用程式真正碰到該頁。第一次存取讓 CPU 發出頁面錯誤時,記憶體管理員會檢查 VAD、PTE、保護屬性與後備存放區,必要時一頁一頁把 RAM 綁上去。1

本文跟著「你碰到的第一個位元組」走到實體 RAM。若想先整理 Working Set 與 Commit 等數字的意義,請看入門篇「Windows 的「記憶體使用量」代表什麼 ── 正確解讀 Working Set、Private Bytes、Commit 與分頁檔」。本系列不重定義那裡用過的術語,而是從機制面追「為什麼會出現那個數字」。

「Windows 記憶體的深層」全 3 回

  1. 第 1 回(本文):虛擬位址與頁面錯誤
    跟著以 VirtualAlloc 配置的區域何時取得實體 RAM。
  2. 第 2 回:實體頁的一生
    跟著離開 Working Set 的頁如何在 Modified、Standby、Free、Zeroed 之間移動。
  3. 第 3 回:區段物件與寫入時複製
    跟著 DLL、檔案對應與共用記憶體為何能共用實體頁。

第 1 回要回答的問題只有一個。

已認可的虛擬位址,在哪一瞬間變成實體 RAM?

預定讀者是想從機制理解 Windows 應用記憶體用量、啟動直後的頁面錯誤、0xC0000005,以及 VMMap 與 PerfMon 數字的開發者與營運人員。前提環境是 Windows 10/11 或現行 Windows Server,必要背景是指標與 VirtualAlloc 基礎;不需要頁面表位元配置或核心偵錯工具經驗。難度為中級。會用到內部結構名稱,但不假設依賴特定 Windows 組建的未公開配置。

1. 先講結論

一般私有記憶體的流程,可以用一句話說完。

Reserve 預留虛擬位址範圍,Commit 對系統認可上限計入 commit charge 以保證將來有地方保存內容,第一次存取的頁面錯誤才指派實體頁。

也就是說,MEM_COMMIT 不是「現在立刻配置 RAM」的命令。Microsoft 的 VirtualAlloc 文件也保證已認可頁的初始內容為零,同時說明實際實體頁要到存取該虛擬位址才會被指派。1

不過,說「Reserve/Commit 只寫進 VAD」也不準確。實務上,Reserve 主要建立代表虛擬位址範圍與屬性的 VAD,Commit 則增加系統的 Commit Total 並記錄該範圍的認可狀態。中間頁面表層級與個別 PTE 會在需要時延遲建立,與實體 RAM 的最終綁定通常發生在第一次存取。

Commit 不是空頭支票,而是 全系統保證將來能把內容保存在 RAM 或適當後備存放區的承諾。把這項承諾一頁一頁兌現的入口,就是頁面錯誤。

Reserve、Commit 與第一次存取時發生的事MEM_RESERVE 把範圍與屬性寫進 VAD,MEM_COMMIT 消耗 Commit Total 以承諾存放,第一次存取的頁面錯誤指派實體頁並加入 Working Set1. MEM_RESERVE2. MEM_COMMIT3. 第一次存取(Touch)把範圍與屬性記進 VAD消耗 Commit Total(尚無實體頁)頁面錯誤把已歸零的實體頁綁到 PTE加入 Working Set 並重執行指令

圖 1: Reserve、Commit 與 Touch 是分開的事件。實體 RAM 只在最後一步、也就是第一次存取時被綁上。

2. 追蹤虛擬頁的三本帳簿

要理解從虛擬位址到實體 RAM 的路徑,必須分清 Windows 保存的三種帳簿。

帳簿 單位 角色
VAD 虛擬位址範圍 管理區域是什麼、Reserve/Commit、保護、與區段的對應
頁面表 / PTE 虛擬頁 表示目前對實體頁的轉譯,或尚未實現的狀態
PFN 資料庫 實體頁 追蹤每頁 RAM 的擁有者、參照與狀態

VAD 握的是 範圍,PTE 握的是 虛擬頁,PFN 資料庫握的是 實體頁。頁面錯誤處理常式交叉核對這些帳簿,決定存取能否繼續。

從虛擬位址到實體 RAM 的三本帳簿虛擬位址由 VAD 以範圍粒度管理、由 PTE 以虛擬頁粒度管理,PTE 的轉譯目標實體頁則由 PFN 資料庫以實體頁粒度追蹤判斷 Reserve/Commit 與保護有效轉譯虛擬位址VAD(範圍帳簿)PTE(虛擬頁帳簿)PFN 資料庫(實體頁帳簿)實體 RAM 頁

圖 2: 粒度不同的三本帳簿。錯誤處理交叉核對 VAD 與 PTE,再把結果反映到 PFN 側。

本文的主角是 VAD 與 PTE。PFN 資料庫會在第 2 回從實體頁那一側來看。

3. Reserve、Commit 與 Touch 是分開的事件

3.1. Reserve ── 先佔住位址

先預留一段連續 256MiB 的虛擬位址範圍。

void* base = VirtualAlloc(
    nullptr,
    256ull * 1024 * 1024,
    MEM_RESERVE,
    PAGE_NOACCESS);

此刻發生的事,只是在處理序的虛擬空間裡把位址佔住,讓其他配置不能用這段範圍。MEM_RESERVE 不會在 RAM 或分頁檔指派實體存放區。1

64 位元處理序的虛擬空間很大,先 Reserve 大範圍、之後只 Commit 需要的部分,實務上很常見。

3.2. Commit ── 承諾將來保得住

接著把已預留的範圍 Commit。

void* committed = VirtualAlloc(
    base,
    256ull * 1024 * 1024,
    MEM_COMMIT,
    PAGE_READWRITE);

成功時,反映在系統 Commit Total——通常也反映在處理序 Private Bytes——的承諾量會增加。即便如此,也不會一次排出 256MiB 的實體頁。一般頁在第一次存取前仍保持實體未指派。12

那麼 Commit 的意義是什麼?在於 系統無法承擔這項承諾時,可以在 Commit 當下回傳失敗,而不是用到一半才失敗

3.3. Touch ── 實體頁變得必要的時候

最後,下面這次指派第一次寫入第一頁。

static_cast<unsigned char*>(base)[0] = 1;

CPU 試著把虛擬位址轉成實體位址,但 PTE 還沒有對實體頁的有效轉譯。頁面錯誤就發生在這裡。

接到控制權的記憶體管理員判斷這是「對已認可、可寫私有頁的第一次存取」,取得已歸零的實體頁、綁到 PTE,並加入 Working Set。然後重執行失敗的寫入指令。

對應用來說只是一次指派,內部卻是 在指派途中進入核心、指派實體頁,再回到同一條指令

4. VAD ── 虛擬空間的範圍帳簿

VAD 是 Virtual Address Descriptor 的縮寫,Windows 用 VAD 樹管理處理序正在使用的位址範圍。用 WinDbg 的 !vad 可以查看起迄 VPN、Commit、保護屬性、Private/Mapped、Control Area 等。3

VAD 記錄的代表性資訊包括下列項目。

  • 位址範圍的起點與終點
  • Private、Mapped、Image 等種類
  • Reserve/Commit 狀態
  • 讀、寫、執行、寫入時複製等保護
  • 與檔案或區段的對應
  • 防護頁等特殊屬性

用範圍管理是為了效率。256MiB 以 4KiB 計是 65,536 頁。與其一開始就為每一頁建完整管理結構,不如在 VAD 裡記下「這段連續範圍是一次預留」,需要時再實現各頁,比較不浪費。

4.1. 找到 VAD 不保證能恢復

「在 VAD 裡就能解決錯誤,不在就存取違規」當作入門說明很方便,但過度簡化。即使找到 VAD,下列情況仍無法繼續一般存取。

  • 只有 Reserve,目標頁尚未認可
  • PAGE_NOACCESS
  • 對唯讀頁寫入
  • 在不可執行頁上執行指令
  • 第一次碰到防護頁
  • 碰到區段有效範圍之外

反過來說,即使 PTE 無效,只要 VAD 與 PTE 的軟體狀態顯示這是正當存取,就能以 demand-zero、Transition 還原、換入或 CoW 解決。更精確的答案是 把 VAD、PTE、保護屬性與存取類型一起判斷

5. 頁面表與 TLB

應用握著的指標是虛擬位址。CPU 要存取 RAM,必須把虛擬頁號轉成實體頁號。那份階層轉譯表就是頁面表,葉項目是 PTE(Page Table Entry)。

有效 PTE 在概念上握有 PFN、讀寫執行保護、使用者模式權限、Accessed/Dirty 等資訊。實際位元配置依 CPU 與 Windows 版本而異。

每次都走頁面表會太慢,所以 CPU 把最近的轉譯快取在 TLB(Translation Lookaside Buffer)。位址轉譯依這個順序進行。

  1. 若 TLB 有轉譯且存取符合該保護,就用那個結果。
  2. 若 TLB 沒有轉譯,CPU 走頁面表。
  3. 若有有效 PTE 且保護也符合,就登錄到 TLB 並繼續執行。
  4. 若沒有有效轉譯,或發生保護違規,就走進頁面錯誤入口。保護檢查在轉譯來自 TLB 時也會做。

這個流程顯示 TLB 未命中與頁面錯誤是兩件事。若只是 TLB 沒有轉譯而 PTE 有效,發生的只是頁面表走訪。反過來說,即使 TLB 已有轉譯,對唯讀頁寫入或在不可執行頁上執行指令這類保護違規,仍會走進頁面錯誤入口。因此對 CoW 頁寫入,即使轉譯已快取,仍可能錯誤。

位址轉譯流程與頁面錯誤入口即使 TLB 有轉譯,保護不符仍走進頁面錯誤入口。TLB 沒有轉譯就走頁面表;有效 PTE 且保護也符合則登錄 TLB 並繼續,無效轉譯或保護違規則走進頁面錯誤入口符合保護違規沒有無效或保護違規記憶體存取TLB 有轉譯嗎?存取符合保護嗎?用該轉譯繼續走進頁面錯誤入口走頁面表有效 PTE 且保護也符合?登錄 TLB 並繼續(無錯誤)

圖 3: TLB 未命中可以用走頁面表解決。轉譯無效或有保護違規時才走進頁面錯誤;保護違規在 TLB 命中時也會發生。

5.1. 無效 PTE 不是空白

即使是無效 PTE 也不是空的。Windows 從無效 PTE 的軟體狀態區分下列情況。

  • 從未實現的 demand-zero 頁
  • 仍留在 RAM 的 Transition 頁
  • 參照 Prototype PTE 的共用頁
  • 保存在分頁檔的私有頁
  • 保護違規或無效區域

CPU 的工作只是判斷「這不是普通的有效轉譯」並交給核心;之後的意義由記憶體管理員補上。

6. 頁面錯誤從頭到尾

讓我們用六個階段跟著對已認可私有頁的第一次寫入。

  1. CPU 嘗試寫入。
    它檢查 TLB 與頁面表,但目標 PTE 沒有有效 PFN。
  2. CPU 發出頁面錯誤。
    把發生錯誤的虛擬位址、讀寫執行類型、使用者/核心,以及問題是缺少轉譯還是保護違規,交給核心。
  3. 記憶體管理員檢查 VAD 與 PTE。
    判斷頁是否已認可、保護是否符合,以及屬於 demand-zero、Transition、共用、換入、CoW 或例外的哪一種。
  4. 若是 demand-zero,取得已歸零的實體頁。
    新交出的頁必須是零,以免洩漏另一個處理序的資料。
  5. 更新 PTE 與 PFN 管理資訊。
    在 PTE 設定 PFN 與保護,把實體頁設為 Active,並加入處理序的 Working Set。
  6. 重執行失敗的指令。
    因為錯誤正常解決,不會把使用者模式例外交出去,應用像平常一樣繼續指派。

ETW 頁面錯誤事件也把 Transition、Demand Zero、Copy-on-Write、Guard Page、Hard Page Fault、Access Violation 記成不同種類。4

所以頁面錯誤一開始就不是「異常」這個詞。它是 CPU 無法走普通路徑轉譯時,請作業系統判斷的共用入口。

頁面錯誤解決的分支記憶體管理員判斷 VAD、PTE、保護屬性與存取類型,分派到 demand-zero、重新接上仍在 RAM 的頁、從後備存放區來的硬錯誤、寫入時複製、防護頁通知或例外第一次存取仍在 RAM需要讀磁碟CoW 寫入防護頁無法解決發生頁面錯誤判斷 VAD、PTE、保護、類型Demand-zero(軟)從 Standby 重接(軟)硬錯誤(磁碟 I/O)複製並更換 PTE清掉防護並通知例外(0xC0000005 等)

圖 4: 從同一入口進來的錯誤,依判斷分成六種結果。防護頁細節見第 9 節。

7. Demand-zero ── 不讀磁碟的軟錯誤

Demand-zero 是第一次碰到已認可私有頁時發生的代表性軟錯誤。Microsoft 的 Working Set 文件也把「處理序第一次參照已配置的虛擬頁」列為軟錯誤的例子。5

Demand-zero 有下列特徵。

  • 不必從磁碟讀原始資料
  • 初始內容為零
  • 綁上可用的實體頁
  • Working Set 與累計 Page Fault Count 增加
  • 單靠這次處理不會增加 Memory\\Pages Input/sec

因此啟動直後 Page Faults/sec 飆高,本身並不代表儲存體是瓶頸。

延遲配置的取捨也值得整理。若 Commit 了 256MiB 卻實際只用 8MiB,把剩下 248MiB 留在 RAM 外是合理的。代價是第一次存取要付錯誤處理成本。對延遲敏感的工作,有一種設計是開始前先碰每一頁做預先錯誤(prefault),但那是先增加 RAM 常駐的取捨。

8. 軟錯誤與硬錯誤

8.1. 軟錯誤

軟錯誤是不必對後備存放區做讀取 I/O 就能解決的錯誤。代表性例子包括下列項目。

  • Demand-zero
  • 把仍在 Standby/Transition 的頁重新接上
  • 接上已在另一處理序 Working Set 的共用頁
  • 接上已預先擷取的頁
  • 原始頁仍常駐的寫入時複製

核心轉換、鎖、PTE/PFN 更新、TLB 一致性等仍有 CPU 成本,但沒有儲存體等待。5

8.2. 硬錯誤

另一方面,需要的頁不在 RAM 任何地方、必須從後備存放區讀取時,就是硬錯誤。讀取來源不只是分頁檔。

  • 被寫出到分頁檔的私有頁
  • 記憶體對應檔
  • EXE 或 DLL 映像
  • 檔案快取參照的資料檔

ETW HardFault 事件包含 FileObject、ReadOffset、ByteCount,因此可以追到實際讀取來源。6

所以 Hard Fault = 讀 pagefile.sys 並不成立。

需要讀後備存放區時,要求會進入 Windows I/O 堆疊。IRP 的發出與完成流程見「Windows I/O 的深層(第 1 回)」,與檔案快取的接點見「Windows I/O 的深層(第 4 回)」。頁在 RAM 裡時,記憶體管理員可以自己回來;不在時就發出 I/O,並讓發生錯誤的執行緒等到完成。

9. 無法解決的錯誤會變成例外

檢查 VAD 與 PTE 後,無法當成正當配置、換入或 CoW 解決的錯誤,會以例外交給使用者模式。

代表性情況是 STATUS_ACCESS_VIOLATION,例外碼 0xC0000005。它發生在對無效位址讀、寫或執行;第一個例外參數指出存取類型,第二個指出違規位址。7

典型模式包括下列項目。

  • 讀取 NULL、已釋放位址或陣列外的位址
  • 對唯讀頁寫入
  • 從 DEP/NX 設成不可執行的頁執行指令
  • 碰到尚未認可的預留範圍

PAGE_GUARD 的意義稍有不同。它是對存取的一次性通知:引發 STATUS_GUARD_PAGE_VIOLATION,用於堆疊成長等情況。8

正常的延遲配置、換入、CoW、防護通知,以及最後的存取違規,從 CPU 看來都聚在同一個頁面錯誤入口。決定結果的是 VAD、PTE、保護屬性與存取類型的組合。

10. 自己動手看

到目前為止的流程,可以在自己的機器上觀察。下面的 C++ 程式 Reserve 256MiB、Commit,對每頁寫一個位元組,最後 Release。每個階段都會等 Enter,方便用 VMMap 與 PerfMon 觀察變化。

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <psapi.h>

#include <cstdio>
#include <cstdlib>

#pragma comment(lib, "Psapi.lib")

constexpr SIZE_T kSize = 256ull * 1024 * 1024;

void PrintMemory(const char* stage)
{
    PROCESS_MEMORY_COUNTERS_EX c{};
    c.cb = sizeof(c);
    if (!GetProcessMemoryInfo(
            GetCurrentProcess(),
            reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&c),
            sizeof(c))) {
        std::printf("GetProcessMemoryInfo failed: %lu\n", GetLastError());
        return;
    }

    std::printf(
        "%-10s WS=%zu MiB  Private=%zu MiB  Faults=%lu\n",
        stage,
        c.WorkingSetSize / 1024 / 1024,
        c.PrivateUsage / 1024 / 1024,
        c.PageFaultCount);
}

void Pause(const char* message)
{
    PrintMemory(message);
    std::puts("Press Enter...");
    (void)std::getchar();
}

int main()
{
    SYSTEM_INFO si{};
    GetSystemInfo(&si);
    std::printf("PID=%lu, page=%lu bytes\n",
                GetCurrentProcessId(), si.dwPageSize);

    void* base = VirtualAlloc(nullptr, kSize, MEM_RESERVE, PAGE_NOACCESS);
    if (!base) {
        std::fprintf(stderr, "Reserve failed: %lu\n", GetLastError());
        return EXIT_FAILURE;
    }
    Pause("reserved");

    if (!VirtualAlloc(base, kSize, MEM_COMMIT, PAGE_READWRITE)) {
        std::fprintf(stderr, "Commit failed: %lu\n", GetLastError());
        VirtualFree(base, 0, MEM_RELEASE);
        return EXIT_FAILURE;
    }
    Pause("committed");

    auto* bytes = static_cast<volatile unsigned char*>(base);
    for (SIZE_T offset = 0; offset < kSize; offset += si.dwPageSize) {
        bytes[offset] = 1;
    }
    Pause("touched");

    if (!VirtualFree(base, 0, MEM_RELEASE)) {
        std::fprintf(stderr, "Release failed: %lu\n", GetLastError());
        return EXIT_FAILURE;
    }
    Pause("released");
}

在 Visual Studio 的 x64 Native Tools Command Prompt 可用下列命令建置。

cl /std:c++20 /EHsc /W4 memory_fault_demo.cpp

10.1. 在 VMMap 看什麼

VMMap 會依類型顯示已預留的虛擬記憶體、Commit、Working Set、Private 與 Shareable。9 各階段預期的變化如下。

階段 預期變化
Reserve Address Space Size 增加,但 Commit/WS 不會同步增加同樣的量
Commit Private Commit 大約增加 256MiB
Touch Working Set 與 Private WS 明顯增加,Fault Count 也增加
Release 目標範圍消失,Commit 與 WS 下降

實際數字會隨執行階段、資安產品、記憶體壓力與觀察時機而變。看的是 各階段之間數字往哪邊移動,不是是否剛好等於 256MiB。

10.2. 在 PerfMon 分開軟與硬

在 PerfMon 把下列計數器放在同一時間軸。

  • Process(<target>)\\Page Faults/sec
  • Memory\\Pages Input/sec
  • Memory\\Page Reads/sec
  • Memory\\Available MBytes
  • Process(<target>)\\Working Set - Private
  • Process(<target>)\\Private Bytes

Process\\Page Faults/sec 同時包含軟錯誤與硬錯誤。另一方面,Memory\\Pages Input/sec 是為了解決硬錯誤而從磁碟讀入的頁數。10

在這個程式的 Touch 階段,Page Faults/sec 應該跳升,而 Pages Input/sec 不該升太多。新認可的頁由 demand-zero 實現,不必從磁碟讀原始資料。

多個處理序共用同一名稱時,PerfMon 的 process#1 這類編號可能在重啟後改變。請用顯示 PID 的計數器交叉核對,或用 Process V2 或 ETW/WPA 依 PID 識別。

11. 實務上要避開的三種誤讀

11.1. 「Commit 上去了,所以是 RAM 洩漏」

Commit 是承諾保存的內容量;還沒碰到的頁可能不常駐在 RAM。要判斷洩漏,請看 Private Bytes 的時間序列、配置的拆解,以及處理結束後數字是否回到基準線。

11.2. 「Page Faults/sec 很高,所以磁碟很慢」

軟錯誤沒有磁碟 I/O。把 Page Faults/secPages Input/sec 與儲存體等待分開看,必要時用 ETW HardFault 事件追到來源檔與堆疊。

11.3. 「清空 Working Set 就能修好洩漏」

把頁從 Working Set 拿掉並不釋放 Commit 或擁有權。該頁會走到 Standby 或 Modified,稍後再錯誤回來。修好洩漏需要配置者執行 VirtualFree、堆積釋放、物件解構等。

被拿掉的實體頁去了哪裡,第 2 回會跟著走。

12. 摘要

  • MEM_RESERVE 預留虛擬位址範圍,但不在 RAM 或分頁檔指派實體區域。1
  • MEM_COMMIT 消耗 Commit 並保證將來能保存內容,但一般實體頁要到第一次存取才指派。12
  • VAD 是範圍帳簿,PTE 是虛擬頁帳簿,PFN 資料庫是實體頁帳簿。
  • TLB 未命中不是頁面錯誤。PTE 有效時,只走頁面表就能解決。
  • Demand-zero、Transition 還原、接上共用頁,是不必磁碟 I/O 就能解決的軟錯誤。5
  • 若必須從分頁檔、DLL、EXE 或對應檔讀取,就是硬錯誤。6
  • 檢查 VAD、PTE 與保護屬性後仍無法解決,會得到 0xC0000005 這類例外。7
  • 判斷效能時不要只看 Page Faults/sec;把 Pages Input/sec、Available、Working Set、Private Bytes 與儲存體等待放在同一時間軸。

續篇是第 2 回,「實體頁的一生:五份清單與分頁檔的真相」。

Commit 承諾變成實體頁之後,我們從 PFN 資料庫與頁清單跟著該頁離開 Working Set 後去了哪裡。

相關文章

相關諮詢領域

小村軟體有限公司承接 Windows 應用程式記憶體用量、存取違規、啟動延遲、分頁與原生程式碼缺陷的調查。

參考連結

  1. Microsoft Learn, VirtualAlloc function. 關於 MEM_RESERVE 預留虛擬位址範圍而不指派實體存放區;MEM_COMMIT 對系統整體記憶體與分頁檔計入 commit charge;已認可頁的初始內容為零;以及實際實體頁要到被存取才指派。  2 3 4 5 6

  2. Microsoft Learn, PERFORMANCE_INFORMATION structure. 關於 CommitTotal 是目前系統 Commit 頁數,以及 CommitLimit 是不擴充分頁檔也能認可的上限。  2

  3. Microsoft Learn, !vad (WinDbg). 關於 !vad 顯示 VAD 樹,並可查看起迄 VPN、Commit、Mapped/Private、保護屬性、Control Area 等。 

  4. Microsoft Learn, PageFault_TypeGroup1 class. 關於 ETW 區分並記錄 Transition Fault、Demand Zero Fault、Copy-on-Write、Guard Page Fault、Hard Page Fault 與 Access Violation。 

  5. Microsoft Learn, Working Set. 關於軟錯誤可不存取後備存放區就能解決,並因另一處理序的 Working Set、Transition、第一次參照的 demand-zero 等而發生。  2 3

  6. Microsoft Learn, PageFault_HardFault class. 關於 HardFault 事件包含 FileObject、ReadOffset、ByteCount、VirtualAddress 與執行緒 ID,因此可追蹤讀取來源。  2

  7. Microsoft Learn, Access Violation C0000005. 關於 0xC0000005 發生在對無效記憶體位址讀、寫或執行,以及例外參數指出存取類型與違規位址。  2

  8. Microsoft Learn, Creating Guard Pages. 關於 PAGE_GUARD 提供頁面存取的一次性通知,並引發 STATUS_GUARD_PAGE_VIOLATION。 

  9. Microsoft Learn, VMMap - Sysinternals. 關於 VMMap 依類型拆解已認可虛擬記憶體,並顯示各類型的 Working Set 與詳細位址圖。 

  10. Microsoft Learn, Performance Analysis of Logs (PAL) Tool. 關於 Memory\\Pages Input/sec 是為了解決硬頁面錯誤而從磁碟讀入的頁數。 

共用相同標籤的最新文章。能以相近的主題延伸理解。

與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。

本文連結到以下服務頁面,歡迎從最接近的入口查看。

常見問題

整理諮詢這個主題時常見的問題。

對 VirtualAlloc 傳入 MEM_COMMIT 的當下就會配置 RAM 嗎?
一般的私有記憶體裡,Commit 會消耗系統的認可餘裕,但對應的實體頁要到第一次存取才會被指派。以寫入首次碰到的頁,是在 demand-zero 錯誤處理中取得實體頁。
頁面錯誤代表異常或有效能問題嗎?
不是。像 demand-zero 或從 Standby 回來這種沒有磁碟 I/O 的軟錯誤是正常運作。判斷效能時,除了 Page Faults/sec,也要看 Pages Input/sec、儲存體等待與 Available MBytes。
TLB 未命中和頁面錯誤是同一件事嗎?
不是。即使 TLB 沒有轉譯,只要頁面表的 PTE 有效,CPU 只是走表並重新登錄轉譯。PTE 無效或發生保護違規時,才會走進頁面錯誤入口。
位址範圍在 VAD 裡,就不會發生存取違規嗎?
不一定。除了 VAD 是否存在,記憶體管理員還會評估 Reserve 與 Commit、讀寫執行保護、防護頁、PTE 狀態等。錯誤無法解決時,會得到 0xC0000005 這類例外。
Page Faults/sec 很高就代表系統 RAM 不夠嗎?
單憑這個看不出來。Page Faults/sec 也包含大量軟錯誤。必須和 Memory\Pages Input/sec、Memory\Page Reads/sec、Available MBytes、磁碟等待時間放在同一時間軸上對照。

作者檔案

本文作者的個人檔案頁面。

Go Komura

小村軟體有限公司 代表

以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。

回到部落格一覽