Tracking down MmSt paged pool usage


A trend that I’ve noticed recently are cases involving paged pool depletion with high MmSt tag usage that remains after trying KB304101 (PoolUsageMaximum). These pool allocations are used by the memory manager for section object prototype PTEs. There are generally only two options when this happens: 1) upgrade to a 64-bit platform, or 2) reduce the size of the volumes. But we may want to know what mapped files are using this memory. Here is how it can be done. Start with !memusage.


5: kd> !memusage

 loading PFN database

loading (100% complete)

Compiling memory usage data (99% Complete).

             Zeroed:  19073 ( 76292 kb)

               Free:      0 (     0 kb)

            Standby: 1468824 (5875296 kb)

           Modified:    368 (  1472 kb)

    ModifiedNoWrite:   1927 (  7708 kb)

       Active/Valid: 605772 (2423088 kb)

         Transition:      0 (     0 kb)

                Bad:      0 (     0 kb)

            Unknown:      0 (     0 kb)

              TOTAL: 2095964 (8383856 kb)

  Building kernel map

  Finished building kernel map

Scanning PFN database - (100% complete)


Following this you will see the list of mapped files and their control areas.


  Usage Summary (in Kb):

Control Valid Standby Dirty Shared Locked PageTables  name

8c62a638  1108  945868  3064     0     0     0  mapped_file( $Mft )


The control area is the address at the far left and has a Segment field that contains the total number of PTEs.


5: kd> dt 8c62a638 _CONTROL_AREA Segment->TotalNumberOfPtes


   +0x000 Segment                    :

      +0x004 TotalNumberOfPtes          : 0x1e8b00


The MmSt allocations contain these PTEs so all we need to do is multiply this by the size of a PTE to get the total size of the MmSt allocations for this control area. Note that there may be multiple allocations for this control area, but this number will reflect the total size all these allocations.


5: kd> ?? 0x1e8b00 * sizeof(nt!_MMPTE)

unsigned int 0xf45800


So now we know the MmSt size in bytes for a single control area, or mapped file. What if we would like to see the totals for all mapped files from the !memusage output? First, place the !memusage output in a text file and remove all header information. You will also need to remove all tail information including the page file and process summaries. Every line should look like these.


8c62a638  1108  945868  3064     0     0     0  mapped_file( $Mft )

8b06ac18   516      0     0     0     0     0    No Name for File


We want to include the “No Name for File” entries since those are valid mapped files even though the name could not be located. Next strip out everything but the control area address. You can use Excel or any other tool that allows you to select and delete columns in a text file. Now we have a file with a single column of all the control areas on the system. The following debugger command script can be used to process this file.


$$ countptes.txt script

r $t2 = 0;


$$ Replace the memusage.txt file name with your file name.

.foreach /f (ca "memusage.txt") {

    r $t1 = @@c++(((nt!_CONTROL_AREA *)(0x${ca}))->Segment->TotalNumberOfPtes);

    .printf "Control Area %p : %d\n", ${ca}, @$t1;

    r $t2 = @$t2 + @$t1;



.printf "Total PTEs : %d\n", @$t2;

.printf "MmSt size  : %d bytes\n", (@$t2 * @@c++(sizeof(nt!_MMPTE)));


The following command will execute the script.


5: kd> $$><countptes.txt


This will show the number of PTEs for each control area, followed by a summary.


Total PTEs : 62790244

MmSt size  : 502321952 bytes


A common high user of MmSt allocations is $Mft. The cache manager will hold the MmSt allocations for these file system metadata files at a cost of up to 4 files per PTE. This technique can be used to determine how much $Mft is using MmSt pool memory by first using findstr at a command prompt to isolate just those values from the !memusage output.


C:\Projects>findstr /c:"$Mft" memusage.txt >mftusage.txt


After stripping out the control area addresses with Excel and running the command script you’ll have the size of the MmSt allocations for just the $Mft files. If this is consuming most of the MmSt bytes then you are limited to the options mentioned at the beginning of this article. There may be other options if something else is the primary user but it will likely involve reducing some heavy load on the system.



Comments (6)

  1. Sushant says:

    This is an awesome explanation.

  2. Rehan says:

    Hi Bryan,

    A very interesting post to say the least. I’m confused about some of the details however.

    I attempted to follow the above instructions to find the total MmSt size and total PTE’s and believe it worked.

    What I don’t understand is the size of MmSt reported seems to be abnormally high,

    like in your example..

    MmSt size  : 502321952 bytes

    That’s close to 500MB! Which certainly can’t correspond to the poolmon paged pool size corresponding to MmSt (unless your on x64 ?).

    Could you please clarify what this size actually corresponds to ? And how we can track the size reported under paged pool in poolmon for the MmSt tag.

  3. Ram Satendra says:


    The mmst tag is related to the pool paged memory, and PTE is related to nonpaged pool memory, if mmst tag is consuming lot of memory then the paged pool is not recycled, this could be most common in windows 2000, all you have to do is trim the memory by making an entry in the registry you can make it at 60%, so that after reaching the maximum memory the paged pool will be recycled

  4. Olegas says:

    Thank you for sharing your knowledge.

    I've slightly modified your script to work with a Windows 2008 R2 64bit dump I was working with, and to accept address file as a script parameter.

    $$ countptes.txt script usage:

    $$  $$>a<"C:Path to ScriptCountPTEs.txt" "C:some foldersome address file.txt"

    .echo Using ${$arg1} as input file.

    r $t2 = 0;

    .foreach /f (ca "${$arg1}") {

       r $t1 = @@c++(((nt!_CONTROL_AREA *)(0x${ca}))->Segment->TotalNumberOfPtes);

       .printf "Control Area %p : %I64dn", ${ca}, @$t1;

       r $t2 = @$t2 + @$t1;


    .printf "Total PTEs : %I64dn", @$t2;

    .printf "MmSt size  : %I64d bytesn", (@$t2 * @@c++(sizeof(ntkrnlmp!_MMPTE)));

  5. @Olegas

    use xperf today:…/Defrag-Tools-48-WPT-Memory-Analysis-Pool

    [Xperf use is limited to relatively small timeframes, so it is not applicable to all scenarios.  We have an article describing it's use for pool leaks at .]

  6. Mohan says:

    I see roughly eighty thousand mapped_file s open from the dump, they all are oldest log files that we have on disk just for retention sake. Nobody should be opening them for any purpose, is there a way to track this further down to a process opening them, might backup/AV..etc? Could I request an expert advice to pinpoint this to a process?  Thanks in advance.

    [Tools such as Process Explorer can be used to determine what processes have handles open to the files.]

Skip to main content