又发现一篇强文,收藏收藏--------------WinCE内存管理(三)

来源:互联网 发布:天津广电网络 编辑:程序博客网 时间:2024/04/29 09:12

又发现一篇强文,收藏收藏--------------WinCE内存管理(三)

(2007-07-19 17:53:10)
转载
  
 
       很明显,以页为单位分配内存对应用程序来说效率是很低的。为了优化内存的使用,应用程序需要以字节为单位分配和释放内存,或者至少以每8字节为单位。系统通过堆来实现这种分配方式。使用堆可以免去处理由Windows CE支持的不同微处理器的不同页面大小。一个应用程序可以简单地在堆中分配一块内存,由系统来处理分配需要的页数。
       就像我前面提到的,堆是系统为应用程序保留的虚拟内存区域。系统提供大量的函数来在堆中分配和释放内存块,并且间隔比页要小(译者注:例如每页大小为4KB,而堆分配可以字节为单位)。当内存由应用程序的堆分配时,系统自动分配调整堆大小来满足需要,当堆中的内存块被释放时,系统会查看是否整页被释放,如果是的话,那么该页将被回收。
       不同于Windows XP,Windows CE只支持在堆中分配固定(fixed)的块。这简化了内存块在堆中的处理,但是这使得堆在分配和释放一段时间后会产生碎片。当堆里已经清空的时候,仍然会占用大量的虚拟内存页,因为系统不能在堆中内存页没有完全释放的时候回收这些页(译者注:因为堆以字节为单位,一页中可能有的块需要被释放,其他的块不需要,所以整页都不会被释放)。
       当应用程序启动的时候,每个程序都会有一个由系统创建的默认或本地堆。本地堆中的内存块,可以通过LocalAllocLocalFree和LocalRealloc来分配,释放和改变大小。一个应用程序也可以建立分离堆。这些堆和本地堆有着相同的属性,但是是通过一组Heapxxxx函数来管理的。
本地堆
       在默认情况下,Windows CE最初会保留192,512字节给本地堆,但是只提交被分配的页。如果应用程序在本地堆中分配了超过188KB,系统将会分配更多的空间给本地堆。增加堆大小将需要一个分离的,不连续的保留地址空间作为堆的附加空间。应用程序不应该假设本地堆被包含在一块虚拟地址空间里。因为Windows CE 的堆只支持固定的块,Windows CE执行的只是Win32本地堆函数的子集,提供必要的分配,改变大小,释放固定的本地堆内存块。
在本地堆中分配内存
       你可以通过一下调用在本地堆中分配一块内存:
HLOCAL LocalAlloc (UINT uFlags, UINT uBytes);
调用返回一个HLOCAL,这是本地内存块的句柄,但是由于内存块是固定分配的,所以返回值可以被简单地看作是一个指向块的指针。
uFlags参数描述了内存块的特征。标志由于Windows CE被限制固定分配操作,只支持以下内存:
LMEM_FIXED
在本地堆中分配一个固定内存块,因为本地堆分配已经固定,所以是多余的。
LMEM_ZEROINIT
初始化内存内容为0
LPTR
合并LMEM_FIXED和LMEM_ZEROINIT标志。
uBytes参数指定了要分配的内存块的大小,以字节为单位。块大小要补齐,但是只针对后面8字节范围。
释放本地堆的内存
       你可以通过以下调用释放内存块:
HLOCAL LocalFree (HLOCAL hMem);
函数需要本地堆内存句柄,成功会返回NULL。如果调用失败,会返回内存块的句柄。
改变和查询本地堆内存的大小
       你可以通过调用改变本地堆的分配:
HLOCAL LocalReAlloc (HLOCAL hMem, UINT uBytes, UINT uFlag);
hMem参数是一个由LocalAlloc返回的指针(句柄)。uBytes参数是内存块的新大小。uFlag参数包含给新内存块的标志。在Windows CE中,有两个新标志与之相关,LMEM_ZEROINIT和LMEM_MOVEABLELMEM_ZEROINIT表示调用函数后内存块中新增加的区域被初始化为0。LMEM_MOVEABLE标志告诉Windows,当内存块增加后,没有合适的空间容纳内存块时,函数可以立即移动内存块。如果没有这个标志,当你没有合适的空间来满足需要的时候,LocalRealloc将会出现out-of-memory的错误而失败,如果你指定了LMEM_MOVEABLE标志,调用将会返回句柄(实际是指向内存块的指针)。
内存块的大小可以通过以下调用查询:
UINT LocalSize (HLOCAL hMem);
返回内存块最少需要的内存大小。像我前面提到的,Windows CE本地堆自动以8个字节来补齐(译者注:就是分配1字节要占8字节)。
分离堆
       为了避免本地堆的碎片,并且如果你要分配连续的内存块,较好的办法是建立分离堆,但将花费一定的时间。一个例子就是,文本编辑器为要编辑的文件建立多个分离堆。当文件被打开或者关闭的时候,堆随之建立和销毁。
在Windows CE下的堆和Windows XP下有着同样的API。唯一值得注意的不同是缺少HEAP_GENERATE- _EXCEPTIONS标志。在Windows XP下,该标志表示系统在分配请求不合适的时候产生一个异常。
建立一个分离堆
       你可以通过以下调用建立一个分离堆。
HANDLE HeapCreate (DWORD flOptions, DWORD dwInitialSize,
                   DWORD dwMaximumSize);
在Windows CE中,第一个参数flOptions必须为空或包含HEAP_NO_SERIALIZE标志。默认情况下,Windows堆管理程序防止一个进程中的两个线程在同意时间访问堆。这个串行参数防止系统用来跟踪堆中内存块分配的堆指针被破坏。在其他版本的Windows中,当你不需要这种保护时可以使用HEAP_NO_SERIALIZE标志。在Windows CE中,该标志是为了兼容性而提供的,所有的堆访问都是串行的(译者注:串行即非并行,只能依次访问)。
其他两个参数,dwInitialSize和dwMaximumSize,指定了最初的大小和预期的堆最大值。dwMaximumSize的值确定虚拟内存空间保留给堆多少页。如果你想让Windows来决定有多少页可以保留,你可以把这个参数设为0。默认一个堆的大小是188KB,dwInitialSize参数决定了有多少这些保留的页将被提交。如果该参数为0,表示堆将一页一页提交。
在分离堆中分配内存
       你可以通过以下调用分配内存
LPVOID HeapAlloc (HANDLE hHeap, DWORD dwFlags, DWORD dwBytes);
       注意,返回值是一个指针,而不是和LocalAlloc函数一样的句柄。分离堆总是分配固定的内存块,甚至在Windows XP和Windows Me中也是一样。第一个参数是通过HeapCreate调用返回的句柄。dwFlags参数可以是两个自说明的(self-explanatory)标志之一HEAP_NO_SERIALIZEHEAP_ZERO_MEMORY。最后一个参数dwBytes指定了要分配的内存块字节数。大小要和DWORD补齐。
释放分离堆中的内存
       你可以通过以下调用释放内存块:
BOOL HeapFree (HANDLE hHeap, DWORD dwFlags, LPVOID lpMem);
dwFlags参数唯一的标志是HEAP_NO_SERIALIZE,当hHeap包含堆句柄时,lpMem参数指向要释放的内存块。
改变和查询分离堆中内存的大小:
       你可以通过以下调用改变堆大小。
    LPVOID HeapReAlloc (HANDLE hHeap, DWORD dwFlags, LPVOID lpMem,
                    DWORD dwBytes);
    dwFlags参数包含三种标志的组合:HEAP_NO_SERIALIZE,HEAP_REALLOC_IN_PLACE_ONLY和HEAP_ZERO_ MEMORY。其中较新的标志是HEAP_REALLOC_IN_PLACE_ONLY,这个参数告诉堆的管理者,找不到要分配的块的空间,重分配操作失败。这个标志方便的地方在于当你有了一些指向内存数据块的指针,并且你不想改变内存块。lpMem参数是一个指向要改变大小的内存块的指针,dwBytes参数是被请求的新内存块的大小。注意,HeapReAlloc中HEAP_REALLOC_IN_PLACE_ONLY标志提供和LocalReAlloc中LMEM_MOVEABLE相反的作用。HEAP_REALLOC_IN_PLACE_ONLY防止在分离堆中对内存块默认的移动操作。而LMEM_MOVEABLE允许本地堆中对内存块的默认移动操作。如果HeapReAlloc成功,就返回一个指向内存块的指针,否则就返回NULL。除非你指定内存块不可重新定位,那么当内存块因为堆中空间不足时将不得不重定位,因此造成返回指针的值将与原来不同。
       要决定实际的内存块大小,你可以作以下调用:
DWORD HeapSize (HANDLE hHeap, DWORD dwFlags, LPCVOID lpMem);
参数就像你想象的:有堆的句柄,单选标志HEAP_NO_SERIALIZE,和指向内存块的指针。
销毁一个分离堆
       你可以通过以下调用完全释放一个堆:
BOOL HeapDestroy (HANDLE hHeap);
在堆中单个的内存块并不需要在销毁堆前释放。
最后一个是写DLL时比较有价值的函数:
HANDLE GetProcessHeap (VOID);
返回的是调用DLL时进程的本地堆的句柄。这个函数允许一个DLL在调用者进程的本地堆中分配内存。GetProcessHeap返回的句柄可以供其他堆调用使用,HeapDestroy除外。