Windows uses something called the Graphics Device Interface (GDI) which is a core component in the Windows OS responsible for display and printer data/communication (according to the Wikipedia article).
Recently, we received some error reports from our software that contained an exception hinting that the host system (Windows Unknown) had run out of free GDI-handles. I vaguely remember the terms GDI- and USER-handle from my childhood years with Windows 3.x programming. So I started looking into this again, seeking to find out what these GDI objects are and what they mean to me - the average desktop Windows C# .NET programmer.
Basically, for every object that needs to be drawn on the user interface (screen or printer), Windows allocates a GDI object which is a native Windows object referencing/representing the object on the device. You could view this as a hook between your normal representation (some
abstract object representing business logic of some kind) and the actual device and pixel representation. Windows does this to give some abstraction between different devices in a way that lets all developers use all devices the same way. So far, everything is good.
When programming C or C++ against native Windows, the programmer is responsible for creating and releasing the GDI objects. When programming in .NET, the creation and destruction of the GDI objects is usually handled “behind the scenes” by the underlying classes and implementation. The typical .NET programmer never even knows about GDI and the objects associated with it and therefore isn’t concerned with the limitations and possible errors surrounding GDI. And then reality hits. Hard!
This was what happened in our program. There is a soft hard limit (yeah – I know – go figure) in apparently all Windows versions limiting the system wide amount of allocated GDI objects to 65,536 (Windows Old-school was 16-bit). There is a (editable, by the way) system registry value setting the maximum number of GDI objects each program can allocate to 10,000. It turns out that our program was hitting this limit. We have some views in our program with some controls and the views are shown on tabs (a GDI object for the main window, one for the tab controller, one for each tab, one for each view, one for each control in the view, one for each label… you get the point!). This approach is fairly standard and so far our program isn’t really using more GDI objects than any other programs out there. The problem was a certain view build from several large composed UI elements. Every time our program recalculates the values in the view – every component is disposed and thrown away – at least that is what we thought. It turns out that there are some limitations on what the .NET garbage collector can handle. Especially circular references seem to be a problem. This caused the disposed views to not be garbage collected – which meant that they kept their GDI object reference. This in turn caused the GDI objects to pile up – making the program run a bit slower – and finally hitting the magical limit of 10,000 for the program instance – causing the program to crash!
This is (of course) mainly our own fault – we the developers should’ve had the knowledge and wisdom to prevent this from ever happening. But I can’t help partly blame .NET for hiding the fact that GDI objects even exist and definitely for failing to release them even after the view for which they were used had been disposed. I can’t help but imagine a much simpler rendering model for .NET programs where the UI might hold only one reference to a single GDI object (behind the scenes) and the actual rendering is done by a managed engine. That should work. Finally, I find it insane that the soft hard coded value 10,000 can be changed by the user at any time – what if the user reduces it to a much lower number – 1,000 or maybe 500 – this would cause many programs to malfunction.
Finally, I started looking around to find out how many GDI objects other popular programs spawned and this is fairly interesting. The Windows Task Manager will actually show the number of GDI objects per process. When Google Chrome was started from a clean boot with nothing but the default window open, it had three processes with a total of 13 + 42 + 34 = 89 GDI objects. Loading a single website (a danish tabloid news site) made Chrome use four processes and 13 + 175 + 44 + 30 = 262 GDI objects. Opening a second (danish news) site made a total of five processes and 13 + 175 + 56 + 53 + 212 = 509 GDI objects – interesting fact: scrolling to the bottom of the news site made the 212 number rise to 468 – making the grand total of 765 GDI objects! And so far, there are only two tabs open with two web pages – which is really nothing compared to my usual browsing habits! In comparison, our program (having six tabs showing user data) uses about 170 GDI objects. The users ability create more in these views will have a high impact on the number of GDI objects, but I feel quite certain that with no GDI leaks, the program never hit the 10,000 limit. I’m not so sure about Google Chrome though.