Mark Russinovich’s technical blog covering topics such as Windows troubleshooting, technologies and security.
Last time, I covered the limits and how to measure usage of one of the two key window manager resources, USER objects. This time, I’m going to cover the other key resource, GDI objects. As always, I recommend you read the previous posts before this one, because some of the limits related to USER and GDI resources are based on limits I’ve covered. Here’s a full index of my other Pushing the Limits of Windows posts:
Pushing the Limits of Windows: Physical Memory Pushing the Limits of Windows: Virtual Memory Pushing the Limits of Windows: Paged and Nonpaged Pool Pushing the Limits of Windows: Processes and Threads Pushing the Limits of Windows: Handles Pushing the Limits of Windows: USER and GDI Objects – Part 1
Pushing the Limits of Windows: Physical Memory
Pushing the Limits of Windows: Virtual Memory
Pushing the Limits of Windows: Paged and Nonpaged Pool
Pushing the Limits of Windows: Processes and Threads
Pushing the Limits of Windows: Handles
Pushing the Limits of Windows: USER and GDI Objects – Part 1
GDI objects represent graphical device interface resources like fonts, bitmaps, brushes, pens, and device contexts (drawing surfaces). As it does for USER objects, the window manager limits processes to at most 10,000 GDI objects, which you can verify with Testlimit using the –g switch:
You can look at an individual process’s GDI object usage on the Performance page of its Process Explorer process properties dialog and add the GDI Objects column to Process Explorer to watch GDI object usage across processes:
Also like USER objects, 16-bit interoperability means that USER objects have 16-bit identifiers, limiting them to 65,535 per session. Here’s the desktop as it appeared when Testlimit hit that limit on a Windows Vista 64-bit system:
Note the Start button on the bottom left where it belongs, but the rest of the task bar at the top of the screen. The desktop has turned black and the sidebar has lost most of its color. Your mileage may vary, but you can see that bizarre things start to happen, potentially making it impossible to interact with the desktop in a reliable way. Here’s what the display switched to when I pressed the Start button:
Unlike USER objects, GDI objects aren’t allocated from desktop heaps; instead, on Windows XP and Windows Server 2003 systems that don’t have Terminal Services installed, they allocate from general paged pool; on all other systems they allocate from per-session session pool.
The kernel debugger’s “!vm 4” command dumps general virtual memory information, including session information at the end of the output. On a Windows XP system it shows that session paged pool is unused:
On a Windows Server 2003 system without Terminal Services, the output is similar:
The GDI object memory limit on these systems is therefore the paged pool limit, as described in my previous post, Pushing the Limits of Windows: Paged and Nonpaged Pool. However, when Terminal Services are installed on the same Windows Server 2003 system, you can see from the non-zero session pool usage that GDI objects come from session pool:
The !vm 4 command in the above output also shows the session paged pool maximum and session pool sizes, but the session paged pool maximum and session space sizes don’t display on Windows Vista and higher because they are variable. Session paged pool usage on those systems is capped by either the amount of address space it can grow to or the System Commit Limit, whichever is smaller. Here’s the output of the command on a Windows 7 system showing the current session paged pool usage by session:
As you’d expect, the main interactive session, Session 1, is consuming the most session paged pool.
You can use the Testlimit tool with the “–g 0” switch to see what happens when the storage used for GDI objects is exhausted. The number you specify after the –g is the size of the GDI bitmap objects Testlimit allocates, but a size of 0 has Testlimit simply try and allocate the largest objects possible. Here’s the result on a 32-bit Windows XP system:
On a Windows XP or Windows Server 2003 that doesn’t have Terminal Services installed you can use the Poolmon utility from Windows Driver Kit (WDK) to see the GDI object allocations by their pool tag. The output of Poolmon the while Testlimit was exhausting paged pool on the WIndows XP system looks like this when sorted by bytes allocated (type ‘b’ in the Poolmon display to sort by bytes allocated), by inference indicating that Gh05 is the tag for bitmap objects on Windows Server 2003:
On a Windows Server 2003 system with Terminal Services installed, and on Windows Vista and higher, you have to use Poolmon with the /s switch to specify which session you want to view. Here’s Testlimit executed on a Windows Server 2003 system that has Terminal Services installed:
The command “poolmon /s1” shows the tags with the largest allocation contributing for Session 1. You can see the Gh15 tag at the top, showing that a different pool tag is being used for bitmap allocations:
Note how Testlimit was able to allocate around 58 MB of bitmap data (that number doesn’t account for GDI’s internal overhead for a bitmap object) on the Windows XP system, but only 10MB on the Windows Server 2003 system. The smaller number comes from the fact that session pool on the Windows Server 2003 Terminal Server system is only 32 MB, which is about the amount of memory Poolmon shows attributed to the Gh15 tag. The output of “!vm 4” confirms that session pool for Session1 is been consumed and that subsequent attempts to allocate GDI objects from session pool have failed:
You can also use the !poolused kernel debugger command to look at session pool usage. First, switch to the correct session by using the .process command with the /p switch and the address of a process object that’s connected to the session. To see what processes are running in a particular session, use the !sprocess command. Here’s the output of !poolmon on the same Windows Server 2003 system, where the “c” option to !poolused has it sort the output by allocated bytes:
Unfortunately, there’s no public mapping between the window manager’s heap tags and the objects they represent, but the kernel debugger’s !poolused command uses the triage.ini file from the debugger’s installation directory to print more descriptive information about a tag. The command reports that Gh15 is GDITAG_HMGR_SPRITE_TYPE, which is only slightly more helpful, but others are more clear.
Fortunately, most GDI and USER object issues are limited to a particular process hitting the per-process 10,000 object limit and so more advanced investigation to figure out what process is responsible for exhausting session pool or allocating GDI objects to exhaust paged pool is unnecessary.
Next time I’ll take a look at System Page Table Entries (System PTEs), another key system resource that can has limits that can be hit, especially on Remote Desktop sessions on Windows Server 2003 systems.
This is all pretty terrible... shouldn't there be some limit to avoid a application being able to screw up Windows entirely like testlimit?
Serious, I never have understood why these limits are not hard limits! As you cant go over it.
Cause like James said, someone could use this for a very simple DOS attack.
Same with applications using 100% CPU and running in real time. So stupid that that is allowed!
what tool do you use to illustrated(decorate) your images...please let us know....
Out of curiosity, why did the pool tag change ? For the OS version change ? Changed to reflect session doing the alloc (also implying OS version, and possible TS dependency) ? Other ?
"Next time I’ll take a look at System Page Table Entries (System PTEs), another key system resource that can has limits that can be hit, especially on Remote Desktop sessions on Windows Server 2003 systems."
Mark, can also look on problem with "The driver is mismanaging system PTEs" under XP x64 SP2, described here -
From the Blog, "As it does for USER objects, the window manager limits processes to at most 10,000 GDI objects."
In Part 1 of the blog Mark states, "A basic limitation imposed by the window manager is that no process can create more than 10,000 USER objects."
If you note from the screenshot Mark is using several processes to deplete the pools, so in theory one application won't cause this type of problem.
If you are worried about DOS attacks, if someone is able to run code on your computer, a DOS attack would be the last thing you need to be worried about.
As for CPU Usage hitting 100%, Vista and Win7 do take notice even though it can't just terminate random processes that spike or use 100% of the CPU, as appications and games often do shove the CPU usage to 100% for extended periods of time. This would makes gamers really unhappy.
I'm sure Mark could explain both of these better than I, so I won't go into any further details.
On my old system at my last job, it had a strange behavior. When I had more than a certain amount of apps open - Alt-TAB would no longer bring up the task list, but would instead cycle through the applications without showing anything. I wonder what resource it was running out of? Closing a few apps would sort-of "fix" it.
It was definitely an issue though, because when it would happen, if I did a shutdown/restart more often than not it would take 15-20 minutes to complete, but if I didn't, the system would be terribly unstable until I did.
What bugs me about the GDI and USER object limits is that they don't scale with system RAM. If I have an app that is hitting CPU, RAM or disk space limits, I can usually add more of something (or upgrade to 64-bits) to solve the problem.
But USER and GDI handles seem like an artifically low limit that no amount of RAM will help. Most people never hit those limits but when it happens, it really messes things up.
DriverDude: I think the important point here is that applications may have legitimate reasons for needing more CPU, more RAM, or more disk space - but any application that is running out of USER or GDI handles is faulty.
To the best of my knowledge, at any rate, there is no legitimate reason why an application would need lots of USER and/or GDI handles.
Mark: Is the 65k address indentifers limit still valid on a 64bit OS? I was under the impression that the 16bit WOW isnt included in a 64bit OS?
The 16-bit subsystem isn't included in x64 Windows, but that doesn't stop use of a 16-bit (2 byte) unsigned short integer in a program.
@Skip IIRC Windows can be configured to do this, not sure if number of windows open has anything to do with it.
And what about GDIProcessHandleQuota & USERProcessHandleQuota?
I think it's very bad architecture that allows one process to screw up the entire system this easily. Since nowadays GDI objects (at least those explicitly created by the user) are under normal circumstances not passed between processes, this is an area that could probably benefit from a redesign.
Redesign? Why bother? How many times does resource exhaustion ever happen to you?