Inside Windows Page Frame Number (PFN) – Part 1

Introduction (Page Frame Number)

Windows and almost all the OSs use Page Frame Number Database in order to have a track of virtually allocated pages to know which page must be freed or evicted or if a page needs to be cached and etc. All of these kinds of stuff manages through a list, called Page Frame Number (PFN). A long list of explanation about the states of every physically and virtually allocated pages and its corresponding attributes. In the rest of this post, I’ll explain Windows implementation of Page Frame Number with lots of practical examples, the following part describes basic concepts implementations, you should also read the next part in order to see how you can use or change these attributes. If you’re familiar with non-PAE mode and PAE mode systems then I should note that in a non-PAE mode every PFN structure takes 24 bytes while in a PAE mode system this size increases to 28 so if your pages are 4096 bytes then allocates about 24 bytes more to keep tracks of every page. As you can see here:

In non-PAE mode 24 bytes in the PFN database represents each 4 KB page of physical memory – this is a ratio of 170:1. In PAE mode 28 bytes represents each 4 KB page of physical memory – this is a ratio of 146:1. This means that roughly 6 MB or 7 MB is needed in the PFN database to describe each 1 GB of physical memory. This might not sound like much, but if you have a 32-bit system with 16 GB of physical memory, then it requires about 112 MB of the 2 GB of kernel virtual address space just to address the RAM. This is another reason why systems with 16 GB of physical memory or more will not allow the 3GB mode (also known as IncreaseUserVA) which increases the user virtual address space to 3 GB and decreases the kernel virtual address space to 1 GB on 32-bit systems.

One of the benefits of having extended pages (e.g 2MB for every page) is that it needs lower amounts of MMPFN. Before start getting deep into the PFN, please remember the term “Page” is mostly used in the operating system level concepts whereas “Frame” is used in CPU Level concepts, therefore “Page” means virtual page and “Page Frame” means physical page.

PFN Lists

The Page Frame Number consists of lists that describe the state of some pages, there are Active Lists which shows an active page (e.g in working sets or etc), Standby List which means a list that previously backed in the disk and the page itself can be emptied and reused without incurring a disk IO, Modified List which shows that the page is previously modified and somehow must be written to the disk, Freed List, as the name describes, it shows a page that is no longer needed to be maintained and can be freed and finally Zero List that describes a page that is free and has all zeroes (0) in it. A great picture derived from here which shows how the PFN database lists are related to each other : PFN These lists are used to manage memory in “page faults” state in the way that everytime a “page fault” occurs, Windows tries to find an available page form, Zero List, if the list is empty then it gets one from Freed List and zeroes the page and use it, on the other hand, if the Freed List is also empty then it goes to the Standby List and zeroes that page.

The Zero Page Thread

In Windows, there is a thread with the priority of 0 which is responsible for zeroing memory when system is idle and is the only thread in the entire system that runs at priority 0. (which is the lowest available priority because the user threads are at least 1). This thread clears the Freed List whenever is possible. Also, there is a function in Windows called RtlSecureZeroMemory() which frees a location securely but in the kernel perspective nt!KeZeroPages is responsible for freeing the pages. The following picture shows the behavior of zero-thread: Zero-thread Let’s find the Zero Thread! We know that it comes from system process, its priority is 0, this should be enough and nothing more is needed. First try to find System‘s nt!_eprocess:

Now we can see the System‘s threads, the details of our target thread (zero-thread) are:

As you can see, its start address is at nt!MiZeroPageThread and its priority-level is 0 and if we see the call-stack then we can see nt!MiZeroPageThread was called previously. For more information please visit Hidden Costs of Memory Allocation.


The Windows structure for PFN is nt!_MMPFN which you can see below :

As you see, _MMPFN takes 28 bytes. PFN records are stored in the memory based on their physical address order which means you can always calculate the physical address with the help of PFN.

The address of the PFN database is located at nt!MmPfnDatabase, you can use the following example to get your PFN database address in Windbg.


Another very useful command in windbg is !memusage, this command gives almost everything about PFN and pages in your memory layout and its corresponding details (e.g files, fonts, system drivers, DLL modules, executable files including their names and their paging bits modifications). A brief sample of this command is shown below :

Note that !memusage takes a long time to finish its probes. What if you want to know more about these pages? The Windbg help document mentioned :


You can use the !vm extension command to analyze virtual memory use. This extension is typically more useful than !memusage. For more information about memory management, see Microsoft Windows Internals, by Mark Russinovich and David Solomon. (This book may not be available in some languages and countries.) The !pfn extension command can be used to display a particular page frame entry in the PFN database.

Now we want survey among these command in a more detailed perspective. That’s enough for now… I try to make another part that describes the PFN more practically, so make sure to check the blog more frequently.

This Post is written in cooperation with my friend Sina.



Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.