Heaps
Windows maintains pools of memory in heaps. A process can contain several heaps, and you allocate memory from these heaps.One heap is often sufficient, but there are good reasons, explained below, for multiple heaps. If a single heap is sufficient, just use the C library memory management functions (malloc, free, calloc, realloc).Heaps are Windows objects; therefore, they have handles. The heap handle is necessary when you're allocating memory. Each process has its own default heap, which is used by malloc, and the next function obtains its handle.
Return: The handle for the process's heap; NULL on failure.
HANDLE GetProcessHeap (VOID)
Notice that NULL is the return value to indicate failure rather than INVALID_HANDLE_VALUE, which is returned by CreateFile.A program can also create distinct heaps. It is convenient at times to have separate heaps for allocation of separate data structures. The benefits of separate heaps include the following.
- Fairness.
No single thread can obtain more memory than is allocated to its heap. In particular, a memory leak defect, caused by a program neglecting to free data elements that are no longer needed, will affect only one thread of a process.Chapter 7 introduces threads. - Multithreaded performance.
By giving each thread its own heap, contention between threads is reduced, which can substantially improve performance. See Chapter 9. - Allocation efficiency.
Allocation of fixed-size data elements within a small heap can be more efficient than allocating elements of many different sizes in a single large heap. Fragmentation is also reduced. Furthermore, giving each thread a unique heap simplifies synchronization, resulting in additional efficiencies. - Deallocation efficiency.
An entire heap and all the data structures it contains can be freed with a single function call. This call will also free any leaked memory allocations in the heap. - Locality of reference efficiency.
Maintaining a data structure in a small heap ensures that the elements will be confined to a relatively small number of pages, potentially reducing page faults as the data structure elements are processed.
The value of these advantages varies depending on the application, and many programmers use only the process heap and the C library. Such a choice, however, prevents the program from exploiting the exception generating capability of the Windows memory management functions (described along with the functions). In any case, the next two functions create and destroy heaps.[3]
[3] In general, create objects of type X with the CreateX system call. HeapCreate is an exception to this rule.
The initial heap size, which can be zero and is always rounded up to a multiple of the page size, determines how much physical storage (in a paging file) is committed to the heap (that is, the required space is allocated from the heap) initially, rather than on demand as memory is allocated from the heap. As a program exceeds the initial size, additional pages are committed automatically up to the maximum size. Because the paging file is a limited resource, deferring commitment is a good practice unless it is known ahead of time how large the heap will become. dwMaximumSize, if nonzero, determines the heap's maximum size as it expands dynamically. The process heap will also grow dynamically.
Return: A heap handle, or NULL on failure.
HANDLE HeapCreate (
DWORD flOptions,
SIZE_T dwInitialSize,
SIZE_T dwMaximumSize)
The two size fields are of type SIZE_T rather than DWORD. SIZE_T is defined to be either a 32-bit or 64-bit unsigned integer, depending on compiler flags (_WIN32 and _WIN64). SIZE_T was introduced to allow for Win64 migration (see Chapter 16) and can span the entire range of a 32- or 64-bit pointer. SSIZE_T is the signed version.flOptions is a combination of two flags.
- HEAP_GENERATE_EXCEPTIONS
With this option, failed allocations generate an exception to be processed by SEH (see Chapter 4). HeapCreate itself will not cause an exception; rather, functions such as HeapAlloc, which will be explained shortly, cause an exception on failure if this flag is set. - HEAP_NO_SERIALIZE
Set this flag under certain circumstances to get a small performance improvement, as discussed later.
There are several other important points regarding dwMaximumSize.
- If dwMaximumSize is nonzero, the virtual address space is allocated accordingly, even though it may not be committed in its entirety. This is the maximum size of the heap, which is said to be nongrowable. This option limits a heap's size, perhaps to gain the fairness advantage cited previously.
- If, on the other hand, dwMaximumSize is 0, then the heap is growable beyond the initial size. The limit is determined by the available virtual address space not currently allocated to other heaps and swap file space.
Note that heaps do not have security attributes because they are not accessible outside the process. File mapping objects, described later in the chapter, can be secured (Chapter 15) as they allow memory sharing between processes.To destroy an entire heap, use HeapDestroy. This is another exception to the general rule that CloseHandle is the function for removing unwanted handles.
BOOL HeapDestroy (HANDLE hHeap)
hHeap should specify a heap generated by HeapCreate. Be careful not to destroy the process's heap (the one obtained from GetProcessHeap). Destroying a heap frees the virtual memory space and physical storage in the paging file. Naturally, well-designed programs should destroy heaps that are no longer needed.Destroying a heap is also a quick way to free data structures without traversing them to delete one element at a time, although C++ object instances will not be destroyed inasmuch as their destructors are not called. Heap destruction has three benefits.
- There is no need to write the data structure traversal code.
- There is no need to deallocate each individual element.
- The system does not spend time maintaining the heap since all data structure elements are deallocated with a single call.
