Private Bytes Performance Counter -- Beware!

Q: When is a Private Byte not a Private Byte?

A: When it isn't resident.

The Private Bytes counter reports the commit charge of the process. That is to say, the amount of space that has been allocated  in the swap file to hold the contents of the private memory in the event that it is swapped out.  Note: I'm avoiding the word "reserved" because of possible confusion with virtual memory in the reserved state which is not committed.

So, if we are concerned with space allocated in the swap file then the Private Bytes counter is right on the money.  However, that is not usually what we are concerned about.  We're much more interested in memory pressure caused by copies of private bytes in multiple processes.  That is to say we are concerned about the physical memory that has been allocated to hold those private bytes.

Why might/does the operating system allocate space in the swap file to hold the contents of memory whose contents have never become resident and may never become resident?  The answer is not actually so complicated:  Windows cannot deliver an "out of memory" exception/error just because you tried writing to, for instance, a static variable.  The swap space must be pre-allocated at a reasonable time (such as loading a DLL) so that we can deliver an error result at a reasonable time -- the time at which the virtual addresses changed from reserved to committed.

So if we had some static data that could be modified, but usually isn't modified in practice, and maybe isn't even read, you would find that the Private Bytes counter overstated the cost of that storage.  The only real cost is the small bit of housekeeping to track allocated space in the swap file.  No private memory need be allocated.

In contrast, the output of vadump deals directly in what is resident and so, if memory is tight, it may understate the true memory cost because some things may have already been swapped out by the time you ask for the current resident pages.  However, if memory is abundant then swapping is not going to happen and you can get a true picture of all the required pages from vadump.

And now a little data about our current product.  Below is a table indicating the memory usage of the same managed notepad application Mark Russinovich wrote about last April.  The Version 1.1 column shows the cost in kilobytes for the relevant class of memory in our Everett release and Version 2.0 shows the result in our current Whidbey release (actually this data is from a build that's a few weeks old now but it's probably still pretty close).

Summary

                         Version 1.1 Version 2.0 Delta

   Kilobytes by Category Total Priv Total Priv Total Priv

       Page Table 108 108 140 140 32 32

       Other System 32 32 32 32 0 0

    Code/Static 6008 1120 6520 568 512 -552

       Heap 680 680 384 384 -296 -296

      Stack 32 32 40 40 8 8

        Teb 12 12 16 16 4 4

     Mapped Data 488 0 356 0 -132 0

      Other Data 644 640 408 404 -236 -236

       Total Modules 6008 1120 6520 568 512 -552

      Total Dynamic 1856 1364 1204 844 -652 -520

  Total System 140 140 172 172 32 32

     Grand Total 8004 2624 7896 1584 -108 -1040

 

Selected Modules

Version 1.1 Version 2.0 Delta

Kilobytes by Module Total Priv Total Priv Total Priv

sys.windows.forms.ni.dll 668 248 1280 80 +612 -168

       mscorlib.ni.dll 640 320 992 88 +352 -232

system.drawing.ni.dll 176 80 244 32 +244 -48

system.ni.dll 164 84 276 32 +112 -52

mscorwks.dll 724 76 1084 60 +152 -36

             mscorsn.dll 56 8 merged into mscorwks.dll

              fusion.dll 152 12 merged into mscorwks.dll

 

(note that in v1.1 the native images didn't have .ni in the name

 and there were two loaded modules for each ngen'd image)

Some interesting things to notice:  even though the overall size of the .NET runtime modules went up in this test case, private pages are way down (1040k less) and overall memory usage is down because of data savings in Mapped data and Other Data -- meaning there is less general overhead.

So despite the fact that the libraries increased in size this application requires less memory in v2.0 -- and if you ran it alongside some other managed application you'd find yourself very pleased because of the low private bytes.  Most of the memory would be shared so the second managed application would have much less impact on your system.

There are some great individual module results as well. Look at winforms -- even though the overall size of winforms went up, the private bytes fell from around 37% of the total to less than 7% of the total.  The cost of the 2nd and subsequent winforms application has plummeted!

If in contrast, you look at the Private Bytes counter, you'll find that, as reported earlier, it's in the 7888k range.  But I don't find that to be representative of memory pressure.  That potentially private memory never makes an appearance.

The Private Bytes counter is definately overstating the true cost so take it with a grain of salt, just like everything else you read on my blog.  :)