Windows also provides a fast memory allocation mechanism called look-aside lists. The basic difference between pools and look-aside lists is that while general pool allocations can vary in size, a look-aside list contains only fixed-sized blocks. Although the general pools are more flexible in terms of what they can supply, look-aside lists are faster because they don’t use any spinlocks.
Executive components and device drivers can create look-aside lists that match the size of frequently allocated data structures by using the ExInitializeNPagedLookasideList and ExInitializePagedLookasideList functions (documented in the WDK). To minimize the overhead of multiprocessor synchronization, several executive subsystems (such as the I/O manager, cache manager, and object manager) create separate look-aside lists for each processor for their frequently accessed data structures. The executive also creates a general per-processor paged and nonpaged look-aside list for small allocations (256 bytes or less).
If a look-aside list is empty (as it is when it’s first created), the system must allocate from paged or nonpaged pool. But if it contains a freed block, the allocation can be satisfied very quickly. (The list grows as blocks are returned to it.) The pool allocation routines automatically tune the number of freed buffers that look-aside lists store according to how often a device driver or executive subsystem allocates from the list—the more frequent the allocations, the more blocks are stored on a list. Look-aside lists are automatically reduced in size if they aren’t being allocated from. (This check happens once per second when the balance set manager system thread wakes up and calls the function ExAdjustLookasideDepth.)
EXPERIMENT: Viewing the System Look-Aside Lists
You can display the contents and sizes of the various system look-aside lists with the kernel debugger !lookaside command. The following excerpt is from the output of this command:lkd> !lookaside
Lookaside "nt!IopSmallIrpLookasideList" @ 81f47c00 "Irps"
Type = 0000 NonPagedPool
Current Depth = 3 Max Depth = 4
Size = 148 Max Alloc = 592
AllocateMisses = 930 FreeMisses = 780
TotalAllocates = 13748 TotalFrees = 13601
Hit Rate = 93% Hit Rate = 94%
Lookaside "nt!IopLargeIrpLookasideList" @ 81f47c80 "Irpl"
Type = 0000 NonPagedPool
Current Depth = 4 Max Depth = 4
Size = 472 Max Alloc = 1888
AllocateMisses = 16555 FreeMisses = 15636
TotalAllocates = 59287 TotalFrees = 58372
Hit Rate = 72% Hit Rate = 73%
Lookaside "nt!IopMdlLookasideList" @ 81f47b80 "Mdl "
Type = 0000 NonPagedPool
Current Depth = 4 Max Depth = 4
Size = 96 Max Alloc = 384
AllocateMisses = 16287 FreeMisses = 15474
TotalAllocates = 72835 TotalFrees = 72026
Hit Rate = 77% Hit Rate = 78%
...
Total NonPaged currently allocated for above lists = 0
Total NonPaged potential for above lists = 3280
Total Paged currently allocated for above lists = 744
Total Paged potential for above lists = 1536
Heap Manager
Most applications allocate smaller blocks than the 64-KB minimum allocation granularity possible using page granularity functions such as VirtualAlloc and VirtualAllocExNuma. Allocating such a large area for relatively small allocations is not optimal from a memory usage and performance standpoint. To address this need, Windows provides a component called the heap manager, which manages allocations inside larger memory areas reserved using the page granularity memory allocation functions. The allocation granularity in the heap manager is relatively small: 8 bytes on 32-bit systems, and 16 bytes on 64-bit systems. The heap manager has been designed to optimize memory usage and performance in the case of these smaller allocations.