正如您所提到的,似乎不可能部分释放一系列保留页面,因为VirtualFree() documentation 声明:
如果 dwFreeType 参数是 MEM_RELEASE,[lpAddress] 必须是当页面区域 [被] 保留时由 VirtualAlloc 函数返回的基地址。
还有:
如果 dwFreeType 参数为 MEM_RELEASE,[dwSize] 必须为 0(零)。
VirtualFree() 本身是内核函数NtFreeVirtualMemory() 的薄包装器。 Its documentation page(与ZwFreeVirtualMemory()相同)也有这种写法。
一种可能的解决方法是将一个大型预订与多个较小的预订分开。例如,假设您通常一次保留 8 MiB 的虚拟地址空间。您可以尝试在 32 个连续的 256 KiB 保留中保留该范围。第一个 256 KiB 预留将包含一个 32 位无符号位字段,如果 ith 获得了 256 KiB 预留:
#define NOMINMAX
#include <windows.h>
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#define RESERVATION_SIZE (256*1024)
typedef struct st_first_reservation {
size_t reservation_size;
uint32_t rfield;
char premaining[0];
} st_first_reservation;
int main()
{
SYSTEM_INFO sys_info = { 0 };
GetSystemInfo(&sys_info);
assert((RESERVATION_SIZE % sys_info.dwPageSize) == 0);
void *vp = VirtualAlloc(NULL, 32*RESERVATION_SIZE, MEM_RESERVE, PAGE_NOACCESS);
if (VirtualFree(vp, 0, MEM_RELEASE) == 0) {
fprintf(stderr, "Error: VirtualFree() failed.\n");
return EXIT_FAILURE;
}
st_first_reservation *pfirst_reservation = (st_first_reservation *) VirtualAlloc(vp, RESERVATION_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pfirst_reservation == NULL) {
pfirst_reservation = (st_first_reservation *) VirtualAlloc(NULL, RESERVATION_SIZE, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
if (pfirst_reservation == NULL) {
fprintf(stderr, "Error: VirtualAlloc() failed.\n");
return EXIT_FAILURE;
}
}
fprintf(stderr, "pfirst_reservation = 0x%p\n", (void *) pfirst_reservation);
pfirst_reservation->reservation_size = RESERVATION_SIZE;
pfirst_reservation->rfield = 1LU;
char *p = (char *) pfirst_reservation;
unsigned i = 1;
for (; i < 32; ++i) {
vp = VirtualAlloc(p += RESERVATION_SIZE, RESERVATION_SIZE, MEM_RESERVE, PAGE_NOACCESS);
if (vp != NULL) {
assert(((void *) vp) == p);
pfirst_reservation->rfield |= 1LU << i;
fprintf(stderr, "Obtained reservation #%u\n", i + 1);
} else {
fprintf(stderr, "Failed to obtain reservation #%u\n", i + 1);
}
}
fprintf(stderr, "pfirst_reservation->rfield = 0x%08x\n", pfirst_reservation->rfield);
return EXIT_SUCCESS;
}
样本输出:
pfirst_reservation = 0x009A0000
获得预订#2
获得预订#3
获得预订#4
获得预订#5
获得预订#6
获得预订#7
获得预订#8
获得预订#9
获得预订#10
获得预订#11
获得预订#12
获得预订#13
获得预订#14
获得预订#15
获得预订#16
获得预订#17
获得预订#18
获得预订#19
获得预订#20
获得预订#21
获得预订#22
获得预订#23
获得预订#24
获得预订#25
获得预订#26
获得预订#27
获得预订#28
获得预订#29
获得预订#30
获得预订#31
获得预订#32
pfirst_reservation->rfield = 0xffffffff
编辑:我发现最好一次免费“预先保留”三十二个 256 KiB 范围,然后尝试重新保留尽可能多的可以。
我更新了上面的代码和示例输出。
在多线程环境中,代码可能会回退到第一次保留的“任何地方”分配。尝试在 32*RESERVATION_SIZE 字节的保留然后释放范围内保留 RESERVATION_SIZE 字节五次左右,最终回到“任何地方”分配是一个好主意。