我想调整由MS窗口的VirtualAlloc分配的内存区域的大小.查看VirtualFree文档,可以仅部分地解除区域,但不可能部分释放它.也就是说,可以释放部分物理内存,但不能释放虚拟内存的一部分.
我知道在这种情况下可能有必要重新分配该区域.但是,复制整个区域的效率会相当低.有没有办法要求Windows分配一个不同大小的新区域,指向相同的内存?
解决方法
正如您所提到的,似乎无法部分释放一系列保留页面,因为
VirtualFree()
documentation声明:
If the dwFreeType parameter is MEM_RELEASE,[lpAddress] must be the base address returned by the 07001 function when the region of pages [was] reserved.
以及:
If the dwFreeType parameter is MEM_RELEASE,[dwSize] must be 0 (zero).
VirtualFree()本身是内核函数NtFreeVirtualMemory()的瘦包装器. Its documentation page(与ZwFreeVirtualMemory()相同)也有此措辞.
一种可能的解决方法是将单个大型预留与多个较小的预留分开.例如,假设您通常一次保留8 MiB的虚拟地址空间.您可以尝试在32个连续的256 KiB预留中保留范围.第一个256 KiB预留将包含一个32位无符号位字段,如果获得第i个256 KiB预留,则设置第i位:
- #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,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,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,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
- Obtained reservation #2
- Obtained reservation #3
- Obtained reservation #4
- Obtained reservation #5
- Obtained reservation #6
- Obtained reservation #7
- Obtained reservation #8
- Obtained reservation #9
- Obtained reservation #10
- Obtained reservation #11
- Obtained reservation #12
- Obtained reservation #13
- Obtained reservation #14
- Obtained reservation #15
- Obtained reservation #16
- Obtained reservation #17
- Obtained reservation #18
- Obtained reservation #19
- Obtained reservation #20
- Obtained reservation #21
- Obtained reservation #22
- Obtained reservation #23
- Obtained reservation #24
- Obtained reservation #25
- Obtained reservation #26
- Obtained reservation #27
- Obtained reservation #28
- Obtained reservation #29
- Obtained reservation #30
- Obtained reservation #31
- Obtained reservation #32
- pfirst_reservation->rfield = 0xffffffff
编辑:我发现最好一次“预先保留”32个256 KiB范围,免费,然后尝试尽可能多地重新预留.
在多线程环境中,代码可能会回退到第一个预留的“任何地方”分配.也许最好尝试在保留然后释放的32 * RESERVATION_SIZE字节范围内保留RESERVATION_SIZE字节五次左右,最后回到“任何地方”分配.