Writing Memory Efficient CE Applications, Page 3
Making Large Allocations for Fairly Limited Durations
Say you have an application that displays large bitmapped graphics. If you are using standard bitmap file formats, you must load the entire file in order to transfer it to the screen. Depending on the color depth of the images, this could amount to a substantial allocation of memory. However, you don't need to hold the memory for very long — only long enough to do the raster drawing. The VirtualAlloc() / VirtualFree() family of functions might be what you need for this kind of job. Below is the declaration for VirtualAlloc():
LPVOID VirtualAlloc( LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect);
The parameters are, in the order shown, the desired base address of the requested allocation, the size in bytes of the allocation request, a flag that specifies whether to reserve or actually commit the allocation, and the access permissions for the allocation. If the first parameter is NULL, the block can be allocated anywhere space is available. The second parameter ( dwSize ) is rounded up to the next full page size. Pay careful attention to the value of dwSize — if it is one byte over the physical page size, you'll end up allocating two pages. You can get the physical page size for a device using this function:
VOID GetSystemInfo( LPSYSTEM_INFO lpSystemInfo);
For our purposes, there are two important values for the flAllocationType flags: MEM_COMMIT and MEM_RESERVE. MEM_RESERVE indicates your intention, at some future point in time, to actually use space. You don't actually have physical access to the space after reserving it. To get access, you must call VirtualAlloc() on the page ( or pages ) with the MEM_COMMIT flag. This two-step strategy has a pair of important advantages: Remember, we're allocating whole pages at a time, which under CE is a very large amount of memory. You call VirtualAlloc() to reserve a block of pages. When you actually need the pages, VirtualAlloc() can commit single pages in the reserved block. This allows you to ensure space will be available before you begin a memory intensive operation. However, it doesn't actually withdraw physical memory from the allocation pool until you need it.
To free space allocated by VirtualAlloc(), you call VirtualFree(). The key thing to know about this call is that it returns freed pages to the allocation pool immediately.
BOOL VirtualFree(LPVOID lpAddress, DWORD dwSize, DWORD dwFreeType);
The parameters, in the order shown, are the base address of the block being freed, the size to free, and a flag that specifies what change to make in the block's allocation status. The flag parameter dwFreeType specifies whether to decommit a page ( dwFreeType = MEM_DECOMMIT) or to completely free the page( dwFreeType = MEM_RELEASE). When a page is decommited, it can be reallocated by the process that reserved it. When it is freed, it is returned to the system memory and can be allocated by anybody.
Porting Tip: Virtual Alloc is best used for large allocations of fairly short duration.
- VirtualAlloc() allocates memory in whole page increments
- Any unused memory inside a page is wasted; it can't be used to satisfy other allocation requests
- Memory can be reserved without withdrawing it from the physical allocation pool
- Memory can't be accessed until it is committed, which removes it from the physical allocation pool
- When you call VirtualFree() to release memory, it is immediately available to other processes.
VirtualAlloc is easy to use, but it allocates a great deal of memory. Because its syntactically similar to the C runtime family (calloc(), malloc(), etc. ), it's tempting to make a quick porting dash to VirtualAlloc(). However, you shouldn't use this scheme unless you can productively exploit most of the page or pages being withdrawn from the allocation pool. If you anticipate allocation patterns that use small amounts of memory for variable durations, but more than you can put in the local program heap, you might be better of setting up a private heap. In the next installment, we'll see how to allocate and use a private heap, and learn about the advantages and disadvantages of this approach to memory allocation.
About the Author
Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, entry time data validation.
In addition to writing for Developer.com, she has written several books including Making Win 32 Applications Mobile.
# # #