How Windows 98 Managed VCache Dynamic Resizing
In Microsoft Windows 98, disk caching was handled by VCache, a 32-bit
protected-mode driver designed to scale its memory usage dynamically
based on active system workload and available physical memory. This
article explains the technical mechanics behind VCache's dynamic cache
resizing, the inherent system memory issues that arose on systems with
larger amounts of RAM, and how manual configuration parameters in
SYSTEM.INI were used to stabilize system performance.
The Architecture of VCache
VCache replaced the older 16-bit real-mode utility SMARTDrive, running as a 32-bit virtual device driver (VxD) tightly integrated with the Windows 98 Virtual Memory Manager (VMM). Instead of reserving a fixed, permanent block of conventional or extended memory for disk caching, VCache shared the same memory pool used by user applications and the operating system itself.
How Dynamic Resizing Worked
Under normal operating conditions, VCache continually negotiated memory consumption with the Virtual Memory Manager using an opportunistic allocation model:
- Expanding the Cache: Whenever disk read or write operations occurred and free physical RAM was detected, VCache requested additional 4 KB memory pages from the VMM. If an application terminated and freed its memory, VCache absorbed that unused RAM to cache recently accessed files, reducing the need for slower mechanical disk reads.
- Shrinking the Cache: When an active application required more physical memory than was currently free, the VMM signaled VCache to release pages. VCache flushed any "dirty" pages (modified data not yet committed to the disk) to storage, discarded "clean" read-only pages, and returned the freed address space to the global pool for application use.
The Limitations of Dynamic Resizing
While efficient on machines with modest RAM (16 MB to 64 MB), VCache's dynamic resizing model exhibited severe limitations as hardware capacities expanded:
- De-allocation Latency: Releasing cache memory was
inherently slower than requesting it. When a memory-heavy application
launched suddenly, VCache could not always de-allocate pages fast
enough. As a result, Windows 98 often resorted to swapping application
data into the disk-based paging file (
WIN386.SWP) instead of waiting for VCache to surrender physical RAM, causing severe system slowdowns (disk thrashing). - The 512 MB+ Address Space Exhaustion: Windows 98 used a 32-bit flat memory model with a single 4 GB virtual address space, dividing it into distinct arenas for user applications and system services. VCache operated within the system arena. On computers equipped with more than 512 MB of physical RAM, VCache could scale to a size where it consumed all available virtual addresses in the system cache arena. This left no contiguous addresses for other drivers or core system operations, resulting in "Out of Memory" errors during startup or when opening multiple windows.
Controlling Resizing with VCache Settings
To resolve the instability caused by unconstrained dynamic resizing,
administrators and power users bypassed automatic management by
explicitly defining cache boundaries in the SYSTEM.INI file
under the [vcache] section.
The primary settings used were:
MinFileCache: Defined the absolute minimum size (in kilobytes) of the cache. The VMM could never shrink the cache below this baseline, even under high memory pressure.MaxFileCache: Set an upper ceiling (in kilobytes) on how large the cache could grow, preventing VCache from consuming all free physical RAM or exhausting the system virtual address space.ChunkSize: Controlled the allocation unit size (in bytes) that VCache used to track cached data, which defaulted to 512 bytes but was often adjusted to optimize page table overhead.
A standard configuration entry designed to limit cache growth on a system looked as follows:
[vcache]
MinFileCache=0
MaxFileCache=524288
ChunkSize=2048By capping MaxFileCache—typically between 25% and 50% of
installed RAM, and rarely exceeding 512 MB (524,288 KB)—users eliminated
memory allocation delays and prevented the address exhaustion bug,
ensuring that dynamic resizing operated strictly within safe operational
boundaries.