May 1998
Minimizing the Memory Footprint of Your Windows CE-based Program
Download WinCEMemory.exe (63KB)
Douglas Boling is an electrical engineer and writer who has worked with PCs since the mid 70's. He is also a contributing editor to Microsoft Interactive Developer and PC Magazine. He is currently writing Programming Windows CE for Microsoft Press.
Each major version
of Windows® has a different mission. Windows 98 and Windows NT® are targeted to the desktop and corporate server markets. Windows CE, on the Other hand, is designed for small platforms. Because of the size and capabilities of the hardware that runs it, Windows CE and applications written for it must be small and extremely memory efficient. A Windows CE machine may have only 1 or 2MB of RAM, which is tiny compared to a typical personal computer that has between 16 and 64MB of RAM. In fact, memory on a Windows CE machine is so scarce that it is often necessary to write programs that conserve memory even to the point of sacrificing the Overall responsiveness of the application.
Fortunately, while the amount of memory may be small in a Windows CE system, the functions available for managing that memory are fairly complete. Windows CE implements almost the full Win32® memory management API available under Windows NT and Windows 98. Windows CE supports virtual memory allocations, local and separate heaps, and even memory-mapped files. Like Windows NT, Windows CE supports a 32-bit flat address space with memory protection between applications. However, since Windows CE was designed for different environments, its underlying memory architecture is different from Windows NT. the Se differences can affect how you design a Windows CE-based application. In this article, I will cover the basic memory architecture of Windows CE. I will also cover the different types of memory allocation available to Windows CE-based programs and how to use each memory type to minimize your application's memory footprint.
Basics
Virtual Memory
The Windows CE Address Space
|
Figure 1 Virtual Address Space |
|
Figure 2 MemDemo |
An Application's Address Space
Different Methods of Memory Allocation
Virtual Memory
|
|
Of course, you should be careful depending too much on autocommitted pages. If there is no RAM left to autocommit, then you will fault. While try/except can catch this, there is nothing you can do to change it. the Only way to guarantee that a page will be physically backed up by RAM is to commit the page manually before you need it. Otherwise, Murphy will insure that at the time you access an autocommit page, the system will be too low on memory to satisfy the request. Before I finish with the virtual memory API, a somewhat subtle point should be made about a Windows CE-based application's virtual memory space. As in Windows NT, virtual memory is reserved in regions that align on 64KB boundaries. Pages within a region can then be committed on a page-by-page basis. It is possible to directly commit a page or a series of pages without first reserving a region of pages, but the page or series of pages committed directly will be aligned on a 64KB boundary. For this reason, it's best to reserve blocks of virtual memory in 64KB chunks, then commit pages within the region as needed. With the limit of a 32MB virtual memory space per process, this leaves a maximum of 32MB / 64KB 1 = 511 virtual memory regions that can be reserved before the system will report that it is out of memory. (In this case, the system is actually out of address space, not memory, but the error code is the same.) The following code attempts to allocate 512 1-page blocks of virtual memory: |
|
Even if there is 512KB of RAM available in the system, VirtualAlloc will fail before the loop completes. It will run out of virtual address space for the application because each 1KB block will be allocated on a 64KB boundary. Since the code, stack, and local heap for an application must also be mapped into the same 32MB virtual address space, available virtual allocation regions usually top out at around 490.
There are clearly better ways to allocate 1KB blocks than making individual calls to VirtualAlloc. However, this example demonstrates a major difference with Windows CE. In Windows NT, applications have a full 2GB virtual address space to work in. With Windows CE, however, you should be aware of the relatively small 32MB virtual address per application.
Heaps
The Stack
Static Data
|
|
the g_hLoadlib variable would end up in the .data segment. The .bss segment contains the uninitialized read-write data. A buffer defined as |
|
would end up in the .bss segment. The final .rdata segment contains the read-only data. Static data defined with the const keyword ends up in the .rdata segment. An example of this would be the structures I use for my message lookup tables: |
|
The .data and .bss blocks are folded into section 0003, which has a total size of 0x2274 (or 8820) bytes. Rounded up to the next page size, the read-write section ends up taking nine pages with 396 bytes not used. So in this example, placing a buffer or two in the static data section of the application would be essentially free. The read-only segment (section 0002) including .rdata ends up being 0x0842 (or 2114) bytes, which takes up three pages with 958 bytes left overalmost an entire page. In this case, moving 75 bytes of constant data from the read-only segment to the read-write segment will save a page of RAM when the application is loaded.
Memory-Mapped Files
|
|
Windows CE-based applications must use this function when opening files for memory-mapped use instead of using CreateFile as under Windows NT or Windows 98. The parameters for this function are similar to those for CreateFile. The file name is the name of the file to read. The dwDesiredAccess parameter, specifying the access rights to the file, must either be zero or GENERIC_READ. Calling CreateFileForMapping and requesting read-write access with the GENERIC_WRITE flag will result in the function failing with an incorrect parameter error code. The security attributes must be NULL, while the hTemplateFile parameter is ignored by Windows CE.
The handle returned by CreateFileForMapping can then be passed to CreateFileMapping to create a memory-mapped file object, albeit one that is read-only. Finally, a view is created by calling MapViewOfFile, which returns a pointer to the memory-mapped file. The code in Figure 5 demonstrates opening a memory-mapped file. Using memory-mapped files for interprocess communication isn't helpful if the applications are restricted to read-only access. Windows CE supports unnamed read-write memory-mapped files. Unnamed, or "page file backed," memory-mapped files are not backed by a page file under Windows CE, nor do they have to be unnamed. The name of this type of memory-mapped file comes from Windows NT where an application could use the paging file of the OS to create a huge, sparse array of virtual pages. The memory-mapped file was unnamed since it was not backed up by a real file on the disk. However, the created object can have a name, and that name can be passed to other processes so they can access the same object. Creating such a memory-mapped file is accomplished by eliminating the call to CreateFileForMapping and passing a 1 in the handle field of CreateFileMapping. Since no file is specified, you must specify the size of the memory-mapped region in the maximum size fields of CreateFileMapping. The following routine creates a 16MB region using a memory-mapped file: |
|
The created object is named by passing a Unicode string to CreateFileMapping. In the previous example, the region is named Bob. This name is global so that if another process opens a mapping object with the same name, the two processes will share the same virtual address space. The memory object created by the code shown above doesn't actually commit 16MB of RAM. Instead, only the address space is reserved. Pages are autocommitted as they are accessed. This allows an application to create a huge, sparse array of pages that take up only as much physical RAM as is needed to hold the data. |
Figure 6 Unnamed Memory-Mapped Objects |
|
Figure 7 Named Memory-Mapped Objects |
Selecting the Proper Memory Type
Managing Low Memory Conditions
Memory Thresholds
From the May 1998 issue of Microsoft Systems Journal.
|