与标志 MEM_RESERVE 一起使用的 VirtualAlloc() 的间接内存消耗
Indirect memory consumption of VirtualAlloc() used with the flag MEM_RESERVE
Microsoft documentation 描述了 VirtualAlloc()
与标志 MEM_RESERVE
:
一起使用时的行为
Reserves a range of the process's virtual address space without allocating any actual physical storage in memory or in the paging file on disk.
VirtualAlloc()
与 MEM_RESERVE
一起使用是否会产生间接增加进程整体内存消耗的副作用?
即使保留的虚拟页面没有关联到物理页面,关于它们的一些信息仍然可以单独存储在数据结构中,并且每个保留的虚拟页面可能占用几个字节的物理内存。如果有人想保留大量虚拟内存(例如 13 TiB),那将是一个问题。
您可以使用此测试程序测试 VirtualAlloc()
与 MEM_RESERVE
一起使用是否间接消耗内存:
#include <windows.h>
#include <stdio.h>
#include <stddef.h>
int main(void) {
void *address = VirtualAlloc(NULL, 13ull << 40ull, MEM_RESERVE, PAGE_NOACCESS);
if (address == NULL)
printf("Reservation failure.\n");
getchar();
return 0;
}
如果您 运行 这个程序,请说明您使用的 Windows 编译器和版本。
Do you know if this also happens on a native Windows environment?
原生 Windows 8(由 Mingw64 编译,统计数据来自任务管理器);您的代码消耗 25.6 MB 的物理内存。通过将其更改为“size_t bytes = 1;
”,物理内存消耗更改为 0.2 MB;这意味着 VirtualAlloc()
本身负责大约 25.4 MB 的物理内存。
请注意,需要一些物理内存来跟踪保留了哪些 area/s;并且因为您可以 free/allocate 单独的页面,这可能不仅仅是“整个区域的开始和结束”(并且可能是该区域中每个 1 GiB 或 2 MiB 或 4 KiB 块的单独标记, 以符合 CPU 用于“虚拟地址到物理地址”转换的表结构。
编辑
根据评论(对不同大小的虚拟内存重复测试),我将这个混乱拼凑在一起(一个循环将分配的大小加倍并报告每个有效大小的结果):
#define __USE_MINGW_ANSI_STDIO 1
#include <windows.h> /* VirtualAlloc() */
#include <psapi.h>
#include <stdio.h> /* getchar() */
#include <stddef.h> /* size_t */
int main(void) {
HANDLE hProcess;
PROCESS_MEMORY_COUNTERS pmc;
void *address;
size_t size = 1;
hProcess = GetCurrentProcess();
if (NULL == hProcess) {
printf( "OpenProcess() failed\n");
getchar();
return 1;
}
while( (address = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS)) != NULL) {
if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) ) {
printf( "Size %zu = Working set size: %u\n", size, pmc.WorkingSetSize );
}
VirtualFree(address, size, MEM_RELEASE);
if(size > SIZE_MAX / 2) {
break;
}
size += size;
}
CloseHandle( hProcess );
getchar();
return 0;
}
在我的电脑上输出是:
Size 1 = Working set size: 1560576
Size 2 = Working set size: 1597440
Size 4 = Working set size: 1597440
Size 8 = Working set size: 1597440
Size 16 = Working set size: 1597440
Size 32 = Working set size: 1597440
Size 64 = Working set size: 1597440
Size 128 = Working set size: 1597440
Size 256 = Working set size: 1597440
Size 512 = Working set size: 1597440
Size 1024 = Working set size: 1597440
Size 2048 = Working set size: 1597440
Size 4096 = Working set size: 1597440
Size 8192 = Working set size: 1597440
Size 16384 = Working set size: 1597440
Size 32768 = Working set size: 1597440
Size 65536 = Working set size: 1597440
Size 131072 = Working set size: 1597440
Size 262144 = Working set size: 1597440
Size 524288 = Working set size: 1597440
Size 1048576 = Working set size: 1597440
Size 2097152 = Working set size: 1597440
Size 4194304 = Working set size: 1597440
Size 8388608 = Working set size: 1597440
Size 16777216 = Working set size: 1597440
Size 33554432 = Working set size: 1597440
Size 67108864 = Working set size: 1597440
Size 134217728 = Working set size: 1597440
Size 268435456 = Working set size: 1597440
Size 536870912 = Working set size: 1597440
Size 1073741824 = Working set size: 1601536
Size 2147483648 = Working set size: 1605632
Size 4294967296 = Working set size: 1613824
Size 8589934592 = Working set size: 1630208
Size 17179869184 = Working set size: 1662976
Size 34359738368 = Working set size: 1728512
Size 68719476736 = Working set size: 1859584
Size 137438953472 = Working set size: 2121728
Size 274877906944 = Working set size: 2646016
Size 549755813888 = Working set size: 3694592
Size 1099511627776 = Working set size: 5791744
Size 2199023255552 = Working set size: 9986048
Size 4398046511104 = Working set size: 18374656
Size 8796093022208 = Working set size: 35151872
Size 17592186044416 = Working set size: 68706304
Size 35184372088832 = Working set size: 135815168
保留内存的成本为每 64 KiB 1 位。它永远不会被释放,即使我们释放该区域。备注:VirtualAlloc(_, _, MEM_RESERVE, _)
的粒度为64KiB。
提交内存的成本为每 4 KiB 8 字节。即使内存未被触及,它也会被消耗,并且只有当我们释放整个保留区域时它才会被释放(一次取消提交是不够的)。备注:指针的大小为8字节,VirtualAlloc(_, _, MEM_COMMIT, _)
的粒度为4KiB(页面大小)。
这已在 Windows 8.1(64 位)和 Windows 10(64 位)上测试,使用 Mingw-w64 作为编译器。这让我感到惊讶,因为在 GNU/Linux,我们可以使用 mmap()
保留和提交整个虚拟地址 space,而不会注意到内存使用量有任何增加。
Microsoft documentation 描述了 VirtualAlloc()
与标志 MEM_RESERVE
:
Reserves a range of the process's virtual address space without allocating any actual physical storage in memory or in the paging file on disk.
VirtualAlloc()
与 MEM_RESERVE
一起使用是否会产生间接增加进程整体内存消耗的副作用?
即使保留的虚拟页面没有关联到物理页面,关于它们的一些信息仍然可以单独存储在数据结构中,并且每个保留的虚拟页面可能占用几个字节的物理内存。如果有人想保留大量虚拟内存(例如 13 TiB),那将是一个问题。
您可以使用此测试程序测试 VirtualAlloc()
与 MEM_RESERVE
一起使用是否间接消耗内存:
#include <windows.h>
#include <stdio.h>
#include <stddef.h>
int main(void) {
void *address = VirtualAlloc(NULL, 13ull << 40ull, MEM_RESERVE, PAGE_NOACCESS);
if (address == NULL)
printf("Reservation failure.\n");
getchar();
return 0;
}
如果您 运行 这个程序,请说明您使用的 Windows 编译器和版本。
Do you know if this also happens on a native Windows environment?
原生 Windows 8(由 Mingw64 编译,统计数据来自任务管理器);您的代码消耗 25.6 MB 的物理内存。通过将其更改为“size_t bytes = 1;
”,物理内存消耗更改为 0.2 MB;这意味着 VirtualAlloc()
本身负责大约 25.4 MB 的物理内存。
请注意,需要一些物理内存来跟踪保留了哪些 area/s;并且因为您可以 free/allocate 单独的页面,这可能不仅仅是“整个区域的开始和结束”(并且可能是该区域中每个 1 GiB 或 2 MiB 或 4 KiB 块的单独标记, 以符合 CPU 用于“虚拟地址到物理地址”转换的表结构。
编辑
根据评论(对不同大小的虚拟内存重复测试),我将这个混乱拼凑在一起(一个循环将分配的大小加倍并报告每个有效大小的结果):
#define __USE_MINGW_ANSI_STDIO 1
#include <windows.h> /* VirtualAlloc() */
#include <psapi.h>
#include <stdio.h> /* getchar() */
#include <stddef.h> /* size_t */
int main(void) {
HANDLE hProcess;
PROCESS_MEMORY_COUNTERS pmc;
void *address;
size_t size = 1;
hProcess = GetCurrentProcess();
if (NULL == hProcess) {
printf( "OpenProcess() failed\n");
getchar();
return 1;
}
while( (address = VirtualAlloc(NULL, size, MEM_RESERVE, PAGE_NOACCESS)) != NULL) {
if ( GetProcessMemoryInfo( hProcess, &pmc, sizeof(pmc)) ) {
printf( "Size %zu = Working set size: %u\n", size, pmc.WorkingSetSize );
}
VirtualFree(address, size, MEM_RELEASE);
if(size > SIZE_MAX / 2) {
break;
}
size += size;
}
CloseHandle( hProcess );
getchar();
return 0;
}
在我的电脑上输出是:
Size 1 = Working set size: 1560576
Size 2 = Working set size: 1597440
Size 4 = Working set size: 1597440
Size 8 = Working set size: 1597440
Size 16 = Working set size: 1597440
Size 32 = Working set size: 1597440
Size 64 = Working set size: 1597440
Size 128 = Working set size: 1597440
Size 256 = Working set size: 1597440
Size 512 = Working set size: 1597440
Size 1024 = Working set size: 1597440
Size 2048 = Working set size: 1597440
Size 4096 = Working set size: 1597440
Size 8192 = Working set size: 1597440
Size 16384 = Working set size: 1597440
Size 32768 = Working set size: 1597440
Size 65536 = Working set size: 1597440
Size 131072 = Working set size: 1597440
Size 262144 = Working set size: 1597440
Size 524288 = Working set size: 1597440
Size 1048576 = Working set size: 1597440
Size 2097152 = Working set size: 1597440
Size 4194304 = Working set size: 1597440
Size 8388608 = Working set size: 1597440
Size 16777216 = Working set size: 1597440
Size 33554432 = Working set size: 1597440
Size 67108864 = Working set size: 1597440
Size 134217728 = Working set size: 1597440
Size 268435456 = Working set size: 1597440
Size 536870912 = Working set size: 1597440
Size 1073741824 = Working set size: 1601536
Size 2147483648 = Working set size: 1605632
Size 4294967296 = Working set size: 1613824
Size 8589934592 = Working set size: 1630208
Size 17179869184 = Working set size: 1662976
Size 34359738368 = Working set size: 1728512
Size 68719476736 = Working set size: 1859584
Size 137438953472 = Working set size: 2121728
Size 274877906944 = Working set size: 2646016
Size 549755813888 = Working set size: 3694592
Size 1099511627776 = Working set size: 5791744
Size 2199023255552 = Working set size: 9986048
Size 4398046511104 = Working set size: 18374656
Size 8796093022208 = Working set size: 35151872
Size 17592186044416 = Working set size: 68706304
Size 35184372088832 = Working set size: 135815168
保留内存的成本为每 64 KiB 1 位。它永远不会被释放,即使我们释放该区域。备注:VirtualAlloc(_, _, MEM_RESERVE, _)
的粒度为64KiB。
提交内存的成本为每 4 KiB 8 字节。即使内存未被触及,它也会被消耗,并且只有当我们释放整个保留区域时它才会被释放(一次取消提交是不够的)。备注:指针的大小为8字节,VirtualAlloc(_, _, MEM_COMMIT, _)
的粒度为4KiB(页面大小)。
这已在 Windows 8.1(64 位)和 Windows 10(64 位)上测试,使用 Mingw-w64 作为编译器。这让我感到惊讶,因为在 GNU/Linux,我们可以使用 mmap()
保留和提交整个虚拟地址 space,而不会注意到内存使用量有任何增加。