虚拟内存方面的API属于页面粒度API,通过这些API分配的内存的最小粒度是64KB
。另外通过前面的《Windows内存体系(2) – 页交换文件》文章,我们可以知道,这些API分配(调拨)的内存区域最初都是位于“页交换文件”
上面,当程序对该区域的某些“页面”(对虚拟内存的管理以页面为单位进行的)进行读写时,才会将这些页面交换到物理内存上面。
从《Windows内存体系(1) – 虚拟地址空间》中我们知道虚拟地址空间要经过预定
和调拨
2个步骤之后才能使用,这2个步骤都可以通过VirtualAlloc
函数实现:
LPVOID VirtualAlloc(
LPVOID lpAddress,
DWORD dwSize,
DWORD flAllocationType,
DWORD flProtect
);
当预定或者调拨的空间我们不在需要时,我们需要调用VirtualFree
来释放该地址空间:
BOOL VirtualFree(
LPVOID lpAddress,
DWORD dwSize,
DWORD dwFreeType
);
下面是的示例演示了在预定、调拨、使用等操作前后,进程的各项内存的占用情况:
#include <windows.h>
int main()
{
SIZE_T size = 1 << 30; // 1GB
// 预定1GB的空间
char *pVirtualAddress = (char *)VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_READWRITE);
if (pVirtualAddress == NULL) {
printf("Reserve 1GB failed.\n");
return 1;
}
// 验证分配粒度是不是64KB
int n = (long)pVirtualAddress % (64*1024);
if (n == 0) {
printf("分配粒度为64K\n");
}
printf("已经预定1GB\n");
getchar(); // 暂停
if (VirtualAlloc(pVirtualAddress, size, MEM_COMMIT, PAGE_READWRITE) == NULL) {
printf("Commit 1GB failed.\n");
return 1;
}
printf("已经调拨1GB\n");
getchar(); // 暂停
// 页面大小为4K,访问2560个页面,即2560*4K = 10MB
//
for (int i = 0; i < 2560; i++) {
char * p = pVirtualAddress + i * (4 * 1024);
*p = 'A'; // 只访问每个页面的第一个字节
}
printf("已经使用前10MB\n");
getchar(); // 暂停
return 0;
}
在程序运行各个阶段进程的内存情况如下图:(“内存专用工作集”表示占用的物理内存的大小,“提交大小”表示调拨的页交换文件的大小)