How Windows 95 Managed USER and GDI System Resources
This article examines how Windows 95 tracked and allocated system resources across the User and Graphics Device Interface (GDI) heaps. While Windows 95 transitioned the operating system to a 32-bit preemptive multitasking environment, it retained a hybrid architecture to maintain backwards compatibility with 16-bit Windows 3.1 applications. This design decision fundamentally shaped how system resources were partitioned, tracked, and reclaimed, resulting in the architecture's notorious resource constraints.
The 16-Bit Legacy and Heap Structure
In Windows 95, system resources were primarily divided into two subsystems: USER (which handled window structures, menus, dialogs, and messaging) and GDI (which handled graphical elements such as fonts, brushes, pens, and device contexts).
Because countless legacy Windows 3.1 applications passed 16-bit
handles directly to USER and GDI functions, these components could not
be fully rewritten as pure 32-bit modules. Doing so would have broken
compatibility with the existing software ecosystem. Consequently, both
USER.EXE and GDI.EXE remained 16-bit dynamic
link libraries that operated alongside their newer 32-bit counterparts
(USER32.DLL and GDI32.DLL).
This architecture meant that core objects still relied on 16-bit local heaps. Under the x86 segmented memory architecture, a 16-bit heap was bound to a single segment, strictly limiting its size to 64 kilobytes regardless of how much physical RAM was installed in the machine.
Heap Reorganization in Windows 95
To alleviate the severe resource exhaustion common in Windows 3.1, Microsoft restructured the heaps for Windows 95:
- 32-Bit Offloading: Many larger data structures were moved out of the 16-bit heaps into the 32-bit flat virtual address space. For example, GDI regions, font metrics, and complex path data were moved to 32-bit storage, freeing up space in the critical 16-bit segments.
- Secondary 16-Bit Heaps: Windows 95 expanded capacity by creating multiple 64 KB heaps instead of relying on a single one for everything. The USER subsystem was split to use separate 64 KB heaps for window handles, menu structures, and general messaging data.
- Handle Thunking: When a 32-bit application created a UI element, a process called "thunking" converted the 32-bit API calls into 16-bit structures when necessary. The handle itself often remained a 16-bit value (or a 32-bit value that mapped directly into a 16-bit space) to ensure cross-process compatibility between 16-bit and 32-bit tasks.
Resource Tracking and Process Ownership
One of the most critical improvements in Windows 95 was the introduction of per-process resource tracking for 32-bit applications.
In Windows 3.1, the operating system had no reliable mechanism to
track which application owned which resource. If a program terminated
unexpectedly or failed to explicitly call functions like
DeleteObject or DestroyWindow, those resources
remained allocated indefinitely, gradually depleting the 64 KB heaps
until the system crashed or was rebooted.
Windows 95 addressed this by tagging resource allocations with the Process Identifier (PID) of the calling application:
- Win32 Applications: When a 32-bit process requested a USER or GDI object, the kernel recorded the allocation against that process’s PID. If the application closed normally or crashed, the process cleanup routines walked the resource tables and automatically released all orphaned GDI objects, window handles, and menu handles back to their respective heaps.
- Win16 Applications: Because 16-bit applications shared a single, cooperative address space and relied on the legacy architecture, the system could not reliably determine process boundaries during unexpected terminations. If a 16-bit program crashed, it frequently left orphaned handles in the 16-bit heaps, leading to permanent resource leaks until a system restart.
The "System Resources" Metric
The well-known "System Resources" percentage displayed in the Windows 95 Resource Meter was not an indicator of free RAM. Instead, it represented the lowest percentage of available space among the most constrained 16-bit heaps: the 16-bit USER heap and the 16-bit GDI heap.
If a system had 128 MB of physical RAM completely free, but an application had exhausted the 64 KB 16-bit GDI brush and pen heap, the Resource Meter would register 0% GDI resources, and subsequent attempts to draw windows or open programs would fail. This constraint remained until Windows transitioned entirely to the Windows NT codebase with Windows XP, which abandoned the hybrid 16-bit heaps entirely in favor of dynamic 32-bit kernel allocations.