The Zone memory system is an internal memory allocator used by Doom for memory management. Due to technology at the time of Doom's release, memory allocation was considered an expensive operation. Presumably due to the inadequacies of memory management functions available in DOS at the time of Doom's development, Doom includes its own memory allocator.
In a normal C program, memory is allocated and deallocated using the C functions
free. The zone memory code contains its own implementations of these,
Rather than calling low-level memory management routines such as
sbrk, zone heaps allocate a single, large, continuous block of RAM using the normal system malloc at the beginning of execution and then divide this large block into smaller ones, keeping all the blocks linked together in a list. When two or more free blocks touch each other, the blocks are merged together to keep the list length short. This helps keep search times for a free block of memory shorter and prevents unnecessary external fragmentation (if blocks are too small, no future allocation may be able to fit into them).
Domain-specific allocation lifetimes
Z_Malloc function differs from the C
malloc function in that it includes support for "tags." Each piece of memory has a particular tag which indicates its purpose and determines its allocation lifetime. Memory blocks are treated differently according to their tags.
The simplest tag is the PU_STATIC tag which specifies a piece of memory which must be explicitly deallocated with the
Z_Free function. From a programmer's point of view, using the functions in this way is identical to using C's memory functions.
More interesting is the PU_CACHE tag. Memory allocated with this tag may be automatically freed back to the system if it runs out of memory. In this way, caching of WAD file data is possible. It is common, for example, to load data from a WAD resource with the PU_STATIC tag, and when operations on that data have completed, change the tag of the area of memory (using the
Z_ChangeTag function) to PU_CACHE. If the system is low on memory, the data may be freed, but otherwise it will be kept in memory for future use. The WAD loading code interacts with the zone memory system to provide this capability, and it was mostly important in Doom as a means to reduce disk reading time.
The other interesting tag is the PU_LEVEL tag. All memory related to the current game level is allocated with the PU_LEVEL tag. When the level exits, the zone memory is searched and all pieces of memory allocated with this tag are freed. This relieves the burden of having to individually free each piece of memory related to the level. It is possible this is inspired by the garbage collector provided by Objective C (Doom was developed on NeXT machines which used Objective C, and the original Doom editor was written in it).
The zone memory system also augmented Doom by providing advanced debugging and diagnostics features that are not available through the standard library memory routines. Amongst these are some of the following:
- Sentinel words to guard against buffer overflows. These are the source of the dreaded "Freed a pointer without ZONEID" messages caused by poorly made maps. All memory blocks have the ZONEID (
0x1d4a11in the original source) written into their header, and if it is wrong during any memory operation, the game engine knows that its heap has been corrupted.
- Complete heap verification. The engine can stop to trace its way across the entire heap and verify that everything is correctly arranged. This is done after extensive operations such as loading save games.
- Ability to dump the heap to a text file to look for errors or for ways to improve memory allocation.
z_zone.h in the Doom source code contains an anecdote that John Carmack considered the zone memory system in Doom "the only stuff that might have been useful for Quake". While it was in fact used in the Quake engine, in a modified form, the WAD code was additionally used as the basis for Quake's PAK files.