The Memory Shell Game
Hello, this is Somak. Today I’d like to drop some Memory Manager info on the blog that I’ve used to communicate in brief (believe it or not) how the system deals with memory. If you are ever faced with checking how much Available Memory you have(or don’t have), poor system performance, questions about page faults, or having a Working Set Trimming performance issue, or just want a primer into how Windows manages memory on the system, be not afraid and read on!
How this memory stuff works
The fastest and most expensive memory is built into the CPU. The next fastest and less expensive is physical RAM. Next we have the hard drive, followed by remote storage or backup. Each step down the ladder adds significantly to access time. For example, physical RAM can be almost one million times faster than a hard disk. Since it is volatile and cost considerably more than hard disk space, we are limited at how much we can put in the system. This limit is far less than the much slower and non-volatile hard disk. Due to these design constraints we have a tiered memory structure in the computer. To achieve the fastest response times and best overall performance, an operating system must efficiently use this tiered memory structure. It must do its best to reduce the need to retrieve data from a slower storage media like the hard disk. It has to do this while juggling the memory and I/O demand from all running processes. The following paragraphs are an overview of how the Memory Manager achieves this in Windows. A more detailed description can be found in Chapter 7 of Microsoft Windows Internals, Fourth Edition (ISBN: 0-7356-1917-4). This book is the great source for how stuff works in Windows.
First let’s lay out some definitions. They will be useful later on when I talk about the interactions. These definitions are high level to maintain brevity.
Virtual Memory – This is a memory that an operating system can address. Regardless of the amount of physical RAM or hard drive space, this number is limited by your processor architecture. On a 32 bit processor you are limited to 4 GB of addressable virtual memory (2^32). With a default installation on a 32 bit box (not using /3GB) the kernel reserves 2GB for itself. Applications are left with 2GB of addressable virtual memory. When applications execute they are only presented with this 2GB of addressable memory. Each application gets its own 2GB virtual memory to play with. If you have 50 processes running, you’ll have 50 independent 2GB Virtual Memory address spaces and one 2GB Virtual Address space for kernel. This is possible because Virtual Memory always exists, but doesn't really exist (hence the term virtual). We basically lie to the application and say, here is 2GB of memory address space for you to use. This memory isn’t allocated until the application explicitly uses it. Once the application uses the page, it becomes committed. A virtual memory page is then translated to a physical memory page. From this translation, the virtual page can reside in physical RAM or on the hard disk.
Physical Memory – This is the physical storage media. It can be physical RAM, the hard disk, optical disks, tape backups, etc. This is anything that can store data. Most times when people talk about Physical Memory, they refer to physical RAM (the memory sticks on your motherboard), but with virtual page translation, physical memory can also be on the hard drive (in your paging file). Physical RAM is limited by your processor architecture.
Committed Memory – When an application touches a virtual memory page (reads/write/programmatically commits) the page becomes a committed page. It is now backed by a physical memory page. This will usually be a physical RAM page, but could eventually be a page in the page file on the hard disk, or it could be a page in a memory mapped file on the hard disk. The memory manager handles the translations from the virtual memory page to the physical page. A virtual page could be in located in physical RAM, while the page next to it could be on the hard drive in the page file.
Commit Limit – This is the maximum amount of memory that all your applications and the OS can commit. If you had 50 applications fully allocate their 2 GB of virtual address space, you would need 100GB of commit limit (ignore kernel memory usage to keep the numbers simple). So 100GB of committed pages can be backed by physical RAM or the hard drive. If you have 100 GB of RAM, you could handle this memory load. In most cases, 100 GB of RAM isn't economically feasible so the Commit Limit is comprised of physical RAM and the page file. If you have 2 GB of physical RAM and 98 GB of page file, then your commit limit would be 100 GB.
Page file – This is the storage area for virtual memory that has been committed. It is located on the hard drive. Since hard drive space is cheaper than physical RAM, it is an inexpensive way to increase the commit limit.
Working Set – This is a set of virtual memory pages (that are committed) for a process and are located in physical RAM. These pages fully belong to the process. A working set is like a "currently/recently working on these pages" list.
Modified pages - Once a virtual memory page leaves the process's working set, it is moved to another list. If the page has been modified, it is placed on the modified page list. This page is in physical RAM. A thread will then write the page to the page file and move it to the standby list.
Standby pages - This is a page that has left the process' working set. This page is in physical RAM. A standby page is like a cache for virtual memory pages. It is still associated with the process, but not in its working set. If the process touches the page, it is quickly faulted back into the working set. That page also has one foot out the door. If another process or cache needs more memory, the process association is broken and it is moved to the free page list. Most of the pages in available memory are actually standby pages. This makes sense when you realize that these pages can be quickly given to another process (hence available), but you should also understand that they are page caches for working sets and can be quickly given back if the process touches the page again. The vast majority of available memory is not wasted or empty memory.
Free pages - When a page is taken off of the standby page list, it is moved to the Free page list. This page is in physical RAM. These pages are not associated with any process. When a process exits, all of its pages are then dumped onto this list. Typically, there is a very small to no amount of free pages hanging around physical RAM.
Zeroed pages - When a free page is zeroed out, it is placed on the Zero page list. This page is in physical RAM. These are the pages that are given to processes that are making memory allocations. Due to C2 security requirements, all pages must be scrubbed before handed to a new process. When the system is idle, a thread will scrub free pages and put them on this list. Only a small amount of zero pages are required to handle the typical small memory allocations of processes. Once this list is depleted, and if there is demand for more pages, we pull pages off of the Free page list and scrub them on the fly. If the Free page list is depleted, then we pull pages off of the standby list, scrub them on the fly and hand them to the new process.
What is Task Manager telling me?
Prior to Windows Vista, Task Manager reports memory usage using accounting methods that you probably are not expecting. It is because of these accounting practices, that we rarely use Task Manager to gauge system performance and memory usage. We typically use it for a quick overview or to kill processes. I highly recommend using Performance Monitor (perfmon.msc) for investigating performance issues. Here's the breakdown of the numbers on the Performance tab:
Total - The is the total physical RAM installed in the system.
Available - This is the total of the Standby, Free and Zeroed list. Free and Zeroed makes sense, but Standby seems odd at first. Standby pages were added to this number because they are available for a quick scrub and given to a new process. So they are technically available (with minimal effort).
System Cache- This is the total of the Standby list and the size of the system working set (which includes the file cache). Standby pages are added to this list because they are cached pages for working sets.
PF Usage - This is the total number of committed pages on the system. It does not tell you how many are actually written to the page file. It only tells you how much of the page file would be used if all committed pages had to be written out to the page file at the same time.
Total - This is the total virtual memory that has been committed. This includes all committed memory for all processes and the kernel.
Limit - This is the maximum amount of committed memory this system can handle. This is a combination of physical RAM and the page file.
Peak – This is the highest amount of memory committed thus far on this system, since boot.
How does this work?
The memory manager optimizes physical RAM usage across the entire system. Since physical RAM is a finite resource, it has to balance sharing this critical resource amongst all process, the kernel and file I/O. It tries to keep disk I/O to a minimum, which results in a more responsive system. It does this by moving pages around to meet the demand of the system.
Typically, large sections of physical RAM are used for file cache. This is cache is necessary to improve disk performance. Without it, disk I/O would make the system crawl along at an nearly unusable pace. The file system cache is just like a working set for a process. Pages removed from the file cache are moved to the standby or modified page list. Many of the standby pages in RAM are probably file cache pages that were removed from its working set. For example, on a file server, if you see 8 GB of available memory, most of these pages are probably standby pages for the file cache. The file cache's working set could be 500 MB, but the 8 GB of standby pages should also be considered part of the file cache.
Now let's take a look at how the memory manager handles processes. While an application is working with its virtual memory pages, the memory manager keeps the pages in the process' working set. Since the vast majority of application do not use all of its memory all the time, some pages will age. Old pages are removed from the working set. If they are modified, they are moved to the modified list. The page is saved to the page file and moved to the standby list. If the page hasn't been modified, it is moved directly to the standby list. These pages will remain on the standby page list until there is a demand for it.
If the application touches the page again, it is soft faulted back into the process' working set. If the process doesn't use the page for a very long time, or if the demand for the page is greater elsewhere, the page is moved off of the standby list. It is disassociated with the process and moved to a the free page list. From the free page list, the page is scrub on demand or lazily and placed on the zero page list. It is from the zero page list that other processes or the kernel or the file cache will get a new page.
If after a very long time the application once again needs a page that is not in its working set, the memory manager will handle the memory fault. If the page is on the standby list, it is quickly put back into the process' working set. If the page is no longer in the standby list, a hard fault occurs. The memory manager issues I/O to the hard disk to read the page(s) from the page file. Once the I/O complete, the page is placed back into the process' work set.
All of this is done to keep physical RAM highly utilized and disk I/O to a minimum. We don't want to allow process to horde physical RAM for pages that are rarely used. The physical RAM must be shared with other processes, the kernel and the file cache. If you see lots of available memory on your system, rest assured that it is not going to waste. The vast majority is on standby lists for processes and the file cache.
Also note that page file usage isn't that bad. The page file allows the Memory Manager to save modified pages before placing the page on the standby list. The page is still in physical RAM and can be quickly faulted back into the process. This method gives the process a chance to reclaim an old page and it allows the page to be quickly used if there is demand elsewhere.
The best way to see the totals of these lists is to use a kernel debugger (live or postmortem). Use the !memusage command and you'll get an output like this:
0: kd> !memusage
loading PFN database
loading (100% complete)
Compiling memory usage data (99% Complete).
Zeroed: 414 ( 1656 kb)
Free: 2 ( 8 kb)
Standby: 864091 (3456364 kb)
Modified: 560 ( 2240 kb)
ModifiedNoWrite: 30 ( 120 kb)
Active/Valid: 182954 (731816 kb)
Transition: 2 ( 8 kb)
Bad: 0 ( 0 kb)
Unknown: 0 ( 0 kb)
TOTAL: 1048053 (4192212 kb)
Of the 4GB of physical RAM, only 1.6 MB are on Zeroed or free pages. 731 MB is in process, system and file cache working sets. 2 MB are on the modified page list. The vast majority, 3.4 GB, is on the standby list. On this server, most people will see 3.4 GB of wasted physical RAM, but you will know better.
What should I be worried about?
Typically you shouldn't be worried about these things, until you have a performance problem. If your system is sluggish or slow to respond or you are getting errors about out of memory, then you need to rely on this information. You will need to collect a performance monitor log of the problem time. If the counter list is daunting, then use the Performance Monitor Wizard to configure the performance monitor log.
Once the log is collected, you'll need to analyze several counters. I'm not going into detail about how to review performance monitor logs this time. I'll save that lengthy topic for another time. For now I'll focus on the counters relevant to the Memory Manager.
One of the biggest reasons for slow performance and sluggish system responsiveness is disk bottleneck. Look at Physical Disk\% Idle Time, Avg. Disk sec/Read and Avg. Disk sec/Write counters. If your system drive is under 50% idle or your disk response times are way above your drive specifications, then you need to investigate further. Look at the Memory\Available Mbytes. You should have a couple hundred Mbytes of Available Memory. This is one of the most important performance monitor counters. If this number drops too low, your standby lists, process working sets and cache will be greatly reduced. You'll need to find out if a process is consuming physical RAM. Check for large process working sets or for large file cache.
You will also need to see if paging is really affecting system performance. Take a look at Memory\Pages Input/sec and correlate that to Physical Disk\Avg. Disk sec/Read. Pages Input/sec is the number of pages being read in from the page file. These are the hard faults (when the page wasn't on the standby list). If your Avg. Disk sec/Read is close to your drive's specification and the drive's idle time is high, than paging really isn't a problem. Small amounts of hard faults are expected as applications will every once in a while re-touch an old page. As long as this I/O is not consistent or the disk can't keep up, you probably will not notice this impact.
You can also look at Memory\Pages Output/sec and Physical Disk\Avg. Disk sec/Write. These are the page commits to the page file when a modified page is removed from a process' working set. As long as the disk can keep up with the writes, this shouldn't be a problem. Remember that once the page is saved to the page file, it is placed on the standby list. If there isn't a great demand for new pages, it can remain on the standby list for a very long time. When the process touches the old virtual page again, it can be soft faulted back into the working set. If there is great demand for memory, you'll see process working sets aggressively being trimmed. Unless there is memory pressure, this is all done with lazy I/O, so you should not see much of an impact from this activity.
The Memory Manager works to meet current demand and prepares for future demand when possible. You need to look at a performance monitor log to see if there is memory pressure on the system. You'll see this in low Available Mbytes and reductions in process working sets. You'll be able to correlate this to increase disk I/O to the page file. If you have established that there is memory pressure on the box, you need to figure where that demand is coming from. Check for working set increases from processes, file cache or bottlenecked disk I/O to data drives.