The Depths of Windows Memory (Part 1) — The Moment a Virtual Address Becomes Physical RAM: A Page Fault from Start to Finish
· Go Komura · Windows, Memory Management, VirtualAlloc, Page Fault, VAD, Performance Monitoring
Passing MEM_COMMIT to VirtualAlloc increases Commit at that moment. Working Set, however, does not necessarily increase by the same amount. So where is the memory you thought you had allocated?
The answer is that most pages do not yet have corresponding physical RAM. Windows delays assigning a physical page until the application actually touches the page. When the first access causes the CPU to raise a page fault, the memory manager examines the VAD, the PTE, the protection attributes, and the backing store, and binds RAM one page at a time if needed.1
This article follows the path that “the first byte you touch” takes until it reaches physical RAM. If you want to sort out the meaning of figures such as Working Set and Commit first, see the introductory article “What Does Windows’ “Memory Usage” Actually Mean? — Correctly Reading Working Set, Private Bytes, Commit, and the Page File”. This series does not redefine the terms used there; it digs into “why the number comes out that way” from the mechanism side.
“The Depths of Windows Memory” — All 3 Parts
- Part 1 (this article): Virtual Addresses and Page Faults
We follow when a region allocated withVirtualAllocobtains physical RAM. - Part 2: The Life of a Physical Page
We follow how a page that leaves the Working Set moves through Modified, Standby, Free, and Zeroed. - Part 3: Section Objects and Copy-on-Write
We follow why DLLs, file mappings, and shared memory can share physical pages.
The question Part 1 answers is just one.
At what moment does a committed virtual address become physical RAM?
Intended readers are developers and operators who want to understand, from the mechanism up, Windows app memory usage, page faults right after startup, 0xC0000005, and the numbers in VMMap and PerfMon. Prerequisites are Windows 10/11 or current Windows Server, and required background is pointers and the basics of VirtualAlloc; you do not need experience with page-table bit layouts or a kernel debugger. The difficulty is intermediate. We use the names of internal structures, but we do not assume undocumented layouts that depend on a particular Windows build.
1. The Bottom Line First
The ordinary private-memory flow, in one line, is this.
Reserve sets aside a virtual address range, Commit charges a commit fee to guarantee a future place to keep the contents, and the page fault on first access assigns the physical page.
In other words, MEM_COMMIT is not an order that says “allocate RAM right now”. Microsoft’s VirtualAlloc documentation also guarantees that the initial contents of a committed page are zero, while explaining that the actual physical page is not assigned until the virtual address is accessed.1
That said, it is also inaccurate to claim that “Reserve/Commit only write to the VAD”. In practice, Reserve mainly creates a VAD that represents the virtual address range and its attributes, and Commit increases the system’s Commit Total and records the committed state of the range. Intermediate page-table levels and individual PTEs are built lazily when needed, and the final binding to physical RAM normally happens on first access.
Commit is not an empty promise; it is a system-wide promise that the contents can be kept in the future in RAM or an appropriate backing store. The entry point that materializes that promise one page at a time is the page fault.
flowchart TB
accTitle: What happens at Reserve, Commit, and first access
accDescr: MEM_RESERVE records the range and attributes in the VAD, MEM_COMMIT consumes Commit Total to promise storage, and the page fault on first access assigns a physical page and adds it to the Working Set
reserve["1. MEM_RESERVE"] --> commit["2. MEM_COMMIT"]
commit --> touch["3. First access (Touch)"]
reserve -.-> vad["Record the range and attributes in the VAD"]
commit -.-> charge["Consume Commit Total (no physical page yet)"]
touch --> fault["Page fault"]
fault --> zero["Bind a zeroed physical page to the PTE"]
zero --> ws["Add it to the Working Set and re-execute the instruction"]
Figure 1: Reserve, Commit, and Touch are separate events. Physical RAM is bound only at the last step, the first access.
2. Three Ledgers That Track a Virtual Page
To understand the path from a virtual address to physical RAM, you need to distinguish the three kinds of ledger Windows keeps.
| Ledger | Unit | Role |
|---|---|---|
| VAD | Virtual address range | Manages what the region is, Reserve/Commit, protection, and section correspondence |
| Page table / PTE | Virtual page | Represents the current translation to a physical page, or an unrealized state |
| PFN database | Physical page | Tracks ownership, references, and state of each RAM page |
The VAD holds information about a range, the PTE about a virtual page, and the PFN database about a physical page. The page-fault handler cross-checks these to decide whether the access can continue.
flowchart TB
accTitle: Three ledgers from a virtual address to physical RAM
accDescr: A virtual address is managed by the VAD at range granularity and by the PTE at virtual-page granularity, and the PFN database tracks the physical page that is the PTE's translation target at physical-page granularity
va["Virtual address"] --> vad["VAD (range ledger)"]
va --> pte["PTE (virtual-page ledger)"]
vad -.->|Judge Reserve/Commit and protection| pte
pte -->|Valid translation| pfn["PFN database (physical-page ledger)"]
pfn --> ram["Physical RAM page"]
Figure 2: Three ledgers at different granularities. Fault handling cross-checks the VAD and the PTE, then reflects the result on the PFN side.
The stars of this article are the VAD and the PTE. We will look at the PFN database from the physical-page side in Part 2.
3. Reserve, Commit, and Touch Are Separate Events
3.1. Reserve — Claiming an Address
First, reserve a contiguous 256MiB virtual address range.
void* base = VirtualAlloc(
nullptr,
256ull * 1024 * 1024,
MEM_RESERVE,
PAGE_NOACCESS);
What happened at this point is only that an address was set aside in the process’s virtual space so that other allocations cannot use this range. MEM_RESERVE assigns no physical storage in RAM or in the page file.1
Because a 64-bit process has a vast virtual space, it becomes practical to Reserve a large range first and Commit only the parts you need later.
3.2. Commit — Promising That It Can Be Kept
Next, Commit the reserved range.
void* committed = VirtualAlloc(
base,
256ull * 1024 * 1024,
MEM_COMMIT,
PAGE_READWRITE);
On success, the promised amount that is reflected in the system’s Commit Total — and usually in the process’s Private Bytes — increases. Even so, 256MiB of physical pages do not line up all at once. Ordinary pages remain physically unassigned until first access.12
So what is the point of Commit? It is that when the system cannot take on the promise, it can return failure at Commit time, rather than in the middle of using the memory.
3.3. Touch — When a Physical Page Becomes Necessary
Finally, the following assignment writes to the first page for the first time.
static_cast<unsigned char*>(base)[0] = 1;
The CPU tries to translate the virtual address to a physical address, but the PTE does not yet have a valid translation to a physical page. A page fault occurs here.
The memory manager that receives control judges this to be “a first access to a committed, writable private page”, obtains a zeroed physical page, binds it to the PTE, and adds it to the Working Set. It then re-executes the write instruction that failed.
From the app it looks like a mere assignment, but internally control enters the kernel in the middle of the assignment, a physical page is assigned, and execution returns to the same instruction.
4. The VAD — the Range Ledger for Virtual Space
VAD stands for Virtual Address Descriptor, and Windows manages a process’s in-use address ranges as a tree of VADs. With WinDbg’s !vad command you can inspect the start and end VPNs, Commit, protection attributes, Private/Mapped, the Control Area, and more.3
Representative information a VAD records includes the following.
- Start and end of the address range
- Kind, such as Private, Mapped, or Image
- Reserve/Commit state
- Protection such as read, write, execute, and Copy-on-Write
- Correspondence to a file or section
- Special attributes such as guard pages
The reason for managing by range is efficiency. 256MiB is 65,536 pages at 4KiB. Rather than building a complete management structure for every page up front, it is less wasteful to hold “this contiguous range is one reservation” in a VAD and materialize pages as they become necessary.
4.1. Finding a VAD Does Not Guarantee Recovery
“If it is in a VAD the fault is resolved; if it is not, you get an access violation” is a convenient entry-level explanation, but it oversimplifies. Even when a VAD is found, ordinary access cannot continue in cases such as the following.
- Reserve only, and the target page is not committed
PAGE_NOACCESS- A write to a read-only page
- Instruction execution from a non-executable page
- First touch of a guard page
- A touch outside the valid range of a section
Conversely, even if the PTE is invalid, if the software state of the VAD and PTE shows a legitimate access, it can be resolved as demand-zero, a Transition restore, a page-in, or CoW. More precisely, the answer is judge VAD, PTE, protection attributes, and access type together.
5. Page Tables and the TLB
The pointer an app holds is a virtual address. For the CPU to access RAM, it must translate a virtual page number into a physical page number. That hierarchical translation table is the page table, and the leaf entry is the PTE (Page Table Entry).
A valid PTE conceptually holds a PFN, read/write/execute protection, user-mode permission, Accessed/Dirty, and similar information. The actual bit layout depends on the CPU and the Windows version.
Walking the page table every time would be far too slow, so the CPU caches recent translations in the TLB (Translation Lookaside Buffer). Address translation proceeds in this order.
- If the TLB has a translation and the access matches that protection, that result is used.
- If the TLB has no translation, the CPU walks the page table.
- If there is a valid PTE and the protection also matches, it is registered in the TLB and execution continues.
- If there is no valid translation, or there is a protection violation, control proceeds to the page-fault entry point. The protection check is also performed when the translation came from the TLB.
As this flow shows, a TLB miss and a page fault are different things. If the only issue is that the TLB has no translation and the PTE is valid, a page-table walk is all that happens. Conversely, even if the TLB has a translation, a protection violation such as a write to a read-only page or instruction execution on a non-executable page proceeds to the page-fault entry point. That is why a write to a CoW page can fault even when the translation is already cached.
flowchart TB
accTitle: Address-translation flow and the page-fault entry point
accDescr: Even if the TLB has a translation, a protection mismatch proceeds to the page-fault entry point. If the TLB has no translation the page table is walked; a valid PTE that also matches protection is registered in the TLB and execution continues, and an invalid translation or protection violation proceeds to the page-fault entry point
access["Memory access"] --> tlb{"Does the TLB have a translation?"}
tlb -->|Yes| perm{"Does the access match the protection?"}
perm -->|Match| go["Continue with that translation"]
perm -->|Protection violation| entry["To the page-fault entry point"]
tlb -->|No| walk["Page-table walk"]
walk --> valid{"Valid PTE and protection also matches?"}
valid -->|Yes| register["Register in the TLB and continue (no fault)"]
valid -->|Invalid or protection violation| entry
Figure 3: A TLB miss can be resolved by a page-table walk. Control proceeds to a page fault when the translation is invalid or there is a protection violation, and a protection violation occurs even on a TLB hit.
5.1. An Invalid PTE Is Not Merely a Blank
Even an invalid PTE is not empty. From the software state of an invalid PTE, Windows distinguishes cases such as the following.
- A demand-zero page that has never been materialized
- A Transition page that remains in RAM
- A shared page that refers to a Prototype PTE
- A private page saved in the page file
- A protection violation or an invalid region
The CPU’s job is only to decide “this is not an ordinary valid translation” and hand it to the kernel; the memory manager supplies the meaning from there.
6. A Page Fault from Start to Finish
Let us follow a first write to a committed private page in six stages.
- The CPU tries to write.
It checks the TLB and the page table, but the target PTE has no valid PFN. - The CPU raises a page fault.
It passes the faulting virtual address, the read/write/execute type, user/kernel, and whether the issue is a missing translation or a protection violation to the kernel. - The memory manager examines the VAD and the PTE.
It decides whether the page is committed, whether the protection matches, and which of demand-zero, Transition, shared, page-in, CoW, or an exception applies. - If it is demand-zero, a zeroed physical page is obtained.
A newly handed-out page must be zero so that another process’s data is not leaked. - The PTE and PFN management information are updated.
The PFN and protection are set in the PTE, the physical page is made Active, and it is added to the process’s Working Set. - The failed instruction is re-executed.
Because the fault resolved normally, no user-mode exception is delivered, and the app continues the assignment as usual.
ETW page-fault events also record Transition, Demand Zero, Copy-on-Write, Guard Page, Hard Page Fault, and Access Violation as distinct kinds.4
So a page fault is not a word that means “abnormal” from the start. It is the common entry point for asking the OS to decide when the CPU could not translate on the ordinary path.
flowchart LR
accTitle: Branching of page-fault resolutions
accDescr: The memory manager judges the VAD, PTE, protection attributes, and access type, and dispatches to demand-zero, reconnecting a page still in RAM, a hard fault from a backing store, copy-on-write, a guard-page notification, or an exception
faultIn["Page fault occurs"] --> judge["Judge VAD, PTE, protection, type"]
judge -->|First access| dz["Demand-zero (soft)"]
judge -->|Still in RAM| soft["Reconnect from Standby (soft)"]
judge -->|Disk read required| hard["Hard fault (disk I/O)"]
judge -->|CoW write| cow["Copy and swap the PTE"]
judge -->|Guard page| guard["Clear guard and notify"]
judge -->|Unresolvable| av["Exception (0xC0000005 etc.)"]
Figure 4: Faults that enter through the same entry point split into six kinds of outcome depending on the judgment. Guard-page details are covered in section 9.
7. Demand-Zero — a Soft Fault That Does Not Read Disk
Demand-zero is the representative soft fault that occurs when a committed private page is first touched. Microsoft’s Working Set documentation also lists “the process refers to an allocated virtual page for the first time” as an example of a soft fault.5
Demand-zero has the following characteristics.
- There is no need to read original data from disk
- The initial contents are zero
- An available physical page is bound
- Working Set and the cumulative Page Fault Count increase
- This handling alone does not increase
Memory\\Pages Input/sec
That is why a spike in Page Faults/sec right after startup does not by itself mean that storage is the bottleneck.
The trade-off of lazy allocation is worth sorting out as well. If you Commit 256MiB and actually use only 8MiB, leaving the remaining 248MiB out of RAM is reasonable. In exchange, first access carries the cost of fault handling. For latency-sensitive work there is a design that touches each page before starting in order to prefault, but that is a trade-off that increases RAM residency up front.
8. Soft Faults and Hard Faults
8.1. Soft Faults
A soft fault is a fault that can be resolved with no read I/O to a backing store. Representative examples include the following.
- Demand-zero
- Reconnecting a page that remains on Standby/Transition
- Connecting a shared page that is in another process’s Working Set
- Connecting a prefetched page
- Copy-on-Write whose original page is resident
There is still CPU cost for the kernel transition, locks, PTE/PFN updates, TLB coherence, and the like, but there is no storage wait.5
8.2. Hard Faults
On the other hand, when the needed page is nowhere in RAM and must be read from a backing store, that is a hard fault. The read source is not only the page file.
- A private page that was written out to the page file
- A memory-mapped file
- An EXE or DLL image
- A data file referenced by the file cache
ETW HardFault events include FileObject, ReadOffset, and ByteCount, so you can track the actual read source.6
Therefore Hard Fault = a read of pagefile.sys is not true.
When a backing-store read is required, the request enters the Windows I/O stack. The flow of IRPs and issue/completion is covered in “The Depths of Windows I/O (Part 1)”, and the junction with the file cache is covered in “The Depths of Windows I/O (Part 4)”. If the page is in RAM the memory manager can return on its own; if it is not, it issues I/O and waits the faulting thread until completion.
9. An Unresolvable Fault Becomes an Exception
A fault that, after examining the VAD and PTE, cannot be resolved as a legitimate allocation, page-in, or CoW is delivered to user mode as an exception.
The representative case is STATUS_ACCESS_VIOLATION, exception code 0xC0000005. It occurs on a read, write, or execute of an invalid address; the first exception parameter indicates the access type and the second the violating address.7
Typical patterns include the following.
- Reading NULL, a freed address, or an address outside an array
- Writing to a read-only page
- Executing an instruction from a page that DEP/NX has made non-executable
- Touching a reserved range that is not committed
PAGE_GUARD has a slightly different meaning. It is a one-shot notification of access: it raises STATUS_GUARD_PAGE_VIOLATION and is used for things such as stack growth.8
Normal lazy allocation, page-in, CoW, guard notification, and a final access violation all gather, from the CPU’s point of view, at the same page-fault entry point. What decides the outcome is the combination of VAD, PTE, protection attributes, and access type.
10. See It for Yourself
You can observe the flow so far on your own machine. The following C++ program Reserves 256MiB, Commits it, writes one byte to each page, and finally Releases. It waits for Enter at each stage so you can observe the changes in VMMap and 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");
}
From Visual Studio’s x64 Native Tools Command Prompt, you can build with the following command.
cl /std:c++20 /EHsc /W4 memory_fault_demo.cpp
10.1. What to Look at in VMMap
VMMap is a tool that displays reserved virtual memory, Commit, Working Set, Private, and Shareable by type.9 The changes to expect at each stage are as follows.
| Stage | Expected change |
|---|---|
| Reserve | Address Space Size increases, but Commit/WS do not increase by the same amount |
| Commit | Private Commit increases by about 256MiB |
| Touch | Working Set and Private WS increase substantially, and the Fault Count also increases |
| Release | The target range disappears, and Commit and WS drop |
Actual numbers vary with the runtime, security products, memory pressure, and when you observe. Look at which way the numbers moved between stages, not at whether they come out to exactly 256MiB.
10.2. Separating Soft and Hard in PerfMon
In PerfMon, place the following counters on the same timeline.
Process(<target>)\\Page Faults/secMemory\\Pages Input/secMemory\\Page Reads/secMemory\\Available MBytesProcess(<target>)\\Working Set - PrivateProcess(<target>)\\Private Bytes
Process\\Page Faults/sec includes both soft and hard faults. Memory\\Pages Input/sec, on the other hand, is the number of pages read from disk to resolve hard faults.10
In this program’s Touch stage, Page Faults/sec should jump while Pages Input/sec should not rise much. Newly committed pages are materialized by demand-zero, so there is no need to read original data from disk.
When several processes share the same name, PerfMon numbers such as process#1 can change across restarts. Cross-check against a counter that displays the PID, or identify by PID with Process V2 or ETW/WPA.
11. Three Misreadings to Avoid in Practice
11.1. “Commit went up, so it is a RAM leak”
Commit is the promised amount of contents to keep; untouched pages may not be resident in RAM. To judge a leak, look at the time series of Private Bytes, the breakdown of allocations, and whether the figure returns to a baseline after processing ends.
11.2. “Page Faults/sec is high, so the disk is slow”
Soft faults involve no disk I/O. Separate Page Faults/sec, Pages Input/sec, and storage wait, and if needed follow the source file and stack with ETW HardFault events.
11.3. “Emptying the Working Set will fix the leak”
Removing a page from the Working Set does not release Commit or ownership. The page moves to Standby or Modified and later faults back in. Fixing a leak requires the allocator to perform VirtualFree, a heap free, object destruction, and the like.
Where that removed physical page goes is what we follow in Part 2.
12. Summary
MEM_RESERVEsets aside a virtual address range but assigns no physical region in RAM or the page file.1MEM_COMMITconsumes Commit and guarantees that the contents can be kept in the future, but an ordinary physical page is not assigned until first access.12- The VAD is the range ledger, the PTE the virtual-page ledger, and the PFN database the physical-page ledger.
- A TLB miss is not a page fault. If the PTE is valid, a page-table walk alone resolves it.
- Demand-zero, Transition restore, and connecting a shared page are soft faults that can be resolved with no disk I/O.5
- If a read from the page file, a DLL, an EXE, or a mapped file is required, it is a hard fault.6
- If inspection of the VAD, PTE, and protection attributes cannot resolve the fault, you get an exception such as
0xC0000005.7 - For a performance judgment, do not look at
Page Faults/secalone; look atPages Input/sec, Available, Working Set, Private Bytes, and storage wait on the same timeline.
Continued in Part 2, “The Life of a Physical Page: Five Lists and the Truth About the Page File”.
After the Commit promise has been turned into a physical page, we follow where that page goes when it leaves the Working Set, from the PFN database and the page lists.
Related Articles
- What Does Windows’ “Memory Usage” Actually Mean? — Correctly Reading Working Set, Private Bytes, Commit, and the Page File
- The Depths of Windows I/O (Part 1) — Every Read and Write Becomes an IRP: The Big Picture of the I/O System
- The Depths of Windows I/O (Part 4) — Cache Manager: When Does Your WriteFile Actually Reach the Disk?
- Reading Crash Dumps with WinDbg + SOS — A Practical Guide to Analysis After Collection
- An Introduction to Collecting Windows Crash Dumps - WER/ProcDump/WinDbg
Related Consulting Areas
KomuraSoft LLC handles investigations of Windows application memory usage, access violations, startup delays, paging, and native-code defects.
- Windows Application Development
- Bug Investigation & Root-Cause Analysis
- Legacy Asset Migration
- Contact Us
References
-
Microsoft Learn, VirtualAlloc function. On
MEM_RESERVEreserving a virtual address range without assigning physical storage;MEM_COMMITcharging a commit fee against the system’s overall memory and page file; the initial contents of a committed page being zero; and the actual physical page not being assigned until it is accessed. ↩ ↩2 ↩3 ↩4 ↩5 ↩6 -
Microsoft Learn, PERFORMANCE_INFORMATION structure. On
CommitTotalbeing the current number of system Commit pages, andCommitLimitbeing the upper bound that can be committed without extending the page file. ↩ ↩2 -
Microsoft Learn, !vad (WinDbg). On
!vaddisplaying the VAD tree and letting you inspect start and end VPNs, Commit, Mapped/Private, protection attributes, the Control Area, and more. ↩ -
Microsoft Learn, PageFault_TypeGroup1 class. On ETW distinguishing and recording Transition Fault, Demand Zero Fault, Copy-on-Write, Guard Page Fault, Hard Page Fault, and Access Violation. ↩
-
Microsoft Learn, Working Set. On a soft fault being resolvable without accessing a backing store, and occurring from another process’s Working Set, Transition, first-reference demand-zero, and the like. ↩ ↩2 ↩3
-
Microsoft Learn, PageFault_HardFault class. On a HardFault event including FileObject, ReadOffset, ByteCount, VirtualAddress, and a Thread ID, so that the read source can be tracked. ↩ ↩2
-
Microsoft Learn, Access Violation C0000005. On
0xC0000005occurring on a read, write, or execute of an invalid memory address, and the exception parameters indicating the access type and the violating address. ↩ ↩2 -
Microsoft Learn, Creating Guard Pages. On
PAGE_GUARDproviding a one-shot notification of page access and raisingSTATUS_GUARD_PAGE_VIOLATION. ↩ -
Microsoft Learn, VMMap - Sysinternals. On VMMap breaking down committed virtual memory by type and displaying each type’s Working Set and a detailed address map. ↩
-
Microsoft Learn, Performance Analysis of Logs (PAL) Tool. On
Memory\\Pages Input/secbeing the number of pages read from disk to resolve hard page faults. ↩
Related Articles
Recent articles sharing the same tags. Deepen your understanding with closely related topics.
The Depths of Windows Memory (Part 2) — The Life of a Physical Page: Five Lists and the Truth About the Page File
This article connects the PFN database, Standby, Modified, memory compression, and the page file to explain where a physical page goes af...
What Does Windows' "Memory Usage" Actually Mean? — Correctly Reading Working Set, Private Bytes, Commit, and the Page File
Task Manager's Memory, Working Set, Private Bytes, and Commit are not the same figure. This article explains the relationship between Win...
The Depths of Windows Memory (Part 3) — Section Objects and Copy-on-Write: What DLLs and File Mappings Really Are
This article connects section objects, image and data mappings, the shared cache, and copy-on-write to explain how DLLs and shared memory...
The Depths of Windows Virtualization (Part 3) — Virtual Machines That Boot in Seconds: Why WSL2, Windows Sandbox, and Containers Are So Light
Why do WSL2 and Windows Sandbox start in seconds and feel so light? This article explains the mechanisms, from dynamic base images and di...
The Depths of Windows Virtualization (Part 2) — Memory Even the Kernel Cannot See: How VBS, HVCI, and Credential Guard Work
On a clean install to compatible hardware, VBS is enabled by default and uses the hypervisor and SLAT to create isolation stronger than t...
Related Topics
These topic pages place the article in a broader service and decision context.
Windows Technical Topics
Topic hub for KomuraSoft LLC's Windows development, investigation, and legacy-asset articles.
Where This Topic Connects
This article connects naturally to the following service pages.
Windows App Development
We support Windows desktop applications that involve resident processing, device integration, operational logging, and maintainable structure.
Frequently Asked Questions
Common questions about the topic of this article.
- Does passing MEM_COMMIT to VirtualAlloc allocate RAM at that moment?
- In ordinary private memory, Commit consumes the system's commit headroom, but the corresponding physical page is not assigned until the first access. A page first touched by a write obtains its physical page during demand-zero fault handling.
- Does a page fault mean something is wrong or that you have a performance problem?
- No. Soft faults that involve no disk I/O — such as demand-zero or returning a page from Standby — are normal operation. For a performance judgment, look not only at Page Faults/sec but also at Pages Input/sec, storage wait, and Available MBytes.
- Are a TLB miss and a page fault the same thing?
- They are different. Even if the TLB has no translation, if the page table's PTE is valid the CPU simply walks the table and re-registers the translation. It proceeds to the page-fault entry point when the PTE is invalid or there is a protection violation.
- If the address range is in a VAD, you cannot get an access violation?
- Not necessarily. In addition to whether a VAD exists, the memory manager evaluates Reserve versus Commit, read/write/execute protection, guard pages, the PTE state, and more. If the fault cannot be resolved, you get an exception such as 0xC0000005.
- Does a high Page Faults/sec mean the system is short on RAM?
- You cannot tell from that alone. Page Faults/sec also includes a large number of soft faults. You need to correlate it on the same timeline with Memory\Pages Input/sec, Memory\Page Reads/sec, Available MBytes, and disk wait time.