上图显示了所有的内存分配方式,在我刚开始学习的时候,也不是很明白为什么有着这么多种的分配方式。其实就一句话:越往下面的内存分配方式效率越高,但是应用的复杂度也就越高。
虚拟内存分配可以用以下方法来实现:
分配函数:
LPVOID VirtualAlloc(LPVOID, DWORD, DWORD,DWORD)
LPVOID VirtualAlloc( LPVOID lpAddress, DWORD dwSize, DWORD flAllocationType, DWORD flProtect);
对于用户进程虚拟空间当中的每个page都有3种状态:
a,free:处于此种状态的page,既没有reserved也没有commit,也就是说它还没有跟物理内存做对应,所有应用程序还不能访问。
b,reserved:处于此种状态的页说明已经被该进程预留了,不能再做其它应用了。如果在该进程当中调用如new(), malloc()等函数申请内存空间,返回的地址不会再reserved空间范围之内,因为它已经被预定了。但是目前它仍然不能被访问,因为还没有和物理内存对应起来。
c,committed:经过committed之后的虚拟地址空间,已经与物理地址做了映射,就能够被进程访问了。
当进程终止的时候该提交的内存页被释放,也就是说该物理内存可以被其它进程使用了,当然我们不应该依靠进程终止来释放物理内存空间,而是应该使用virtualFree()手动释放该物理空间,避免因操作不当导致内存泄露。
释放函数:
LPVOID VirtualFree(LPVOID, DWORD, DWORD)
改变和查询访问权限函数:
LPVOID VirtualProtect(LPVOID, DWORD, DWORD,DWORD)
这3个函数具体的参数可以查看MSDN,在这里就不详细介绍了。在使用这3个函数的时候我们需要注意一些问题,用个书上的例子来解释:
for(i = 0; i < 512; i++)
{
pMem[i] = VirtualAlloc(NULL, PAGESIZE,
MEM_RESERVE | MEM_COMMIT,
PAGE_RDWRITE);
}
也许在XP下我们写这种程序也成习惯了,但是这是在Windows CE中,这里我们就需要注意一些问题了:
1.保留的虚拟内存是按照64KB对齐的。
2.提交的虚拟内存是按照页面来对齐的。
大家也许就已经明白过来了,如果我们这样去申请内存,在系统自动内存对齐的情况下我们是会浪费非常多的内存的,在5.0中测试这个程序,分配会失败,在6.0中分配成功,大家可以去试验一下。那么我们正确的做法应该是先保留一个大的内存块,然后再需要用的时候再提交一次。
pMemBase = VirtualAlloc (NULL, PAGESIZE, MEM_RESERVE,
PAGE_REDWRITE);
for(i = 0; i < 512; i++)
{
pMem[i] = VirtualAlloc (pMemBase + (i * PAGESIZE),
PAGESIZE, MEM_COMMIT,
PAGE_REDWRITE);
}
通过这些学习也给了我们一定的启示:
CE 5.0的32*32的局限:
1.尽可能少使用多进程。
2.可以多利用多线程。
3.使用内存映射文件区域申请大内存
虚拟内存分配的64KB对齐:
1.尽可能按照64KB边界申请虚拟地址空间。
2.一次申请,多次提交。
3.使用更上层的动态内存分配函数。
4.合并小的DLL为64KB边界。
堆和栈的内容就不在这里介绍了。
总结
我们已经讨论了不同的内存类型,现在来讨论如何充分利用各种内存类型。
1.对于大的内存块,最好是直接分配虚拟内存。
2.本地堆使用便利,但是需要注意内存碎片。
3.独立堆的优点是当你不使用时可以直接销毁它,从而把内存碎片消灭在萌芽状态。
4.静态数据区域是放一两个缓冲区的好地方,因为页面总是要被分配的。
5.栈使用简单,但是栈的大小只有64KB。