我想调整由MS窗口的VirtualAlloc分配的内存区域。查看VirtualFree文档,可以仅部分地解除区域,但不可能部分释放它。也就是说,可以释放部分物理内存,但不能释放虚拟内存的一部分。
我知道在这种情况下可能需要重新分配该区域。但是,复制整个区域的效率会相当低。有没有办法让windows分配一个不同大小的新区域,指向同一个内存?
答案 0 :(得分:3)
正如您所提到的,由于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位无符号位字段,如果 i th,则 i th 位置1 获得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 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范围,免费,然后尝试重新预订,就像你一样多可以。
我更新了上面的代码和示例输出。
在多线程环境中,代码可能会回退到第一个预留的“任何地方”分配。也许最好尝试将RESERVATION_SIZE
个字节保留在保留 - 然后释放的32*RESERVATION_SIZE
字节范围内五次左右,最后再回到“任何地方”分配。
答案 1 :(得分:1)
不是答案,但我不得不问:
考虑到您所处的痛苦,VirtualAlloc()的性能命中率以及代码的不可移植性;与VIrtualAlloc()给出的任何值相比,您是否可以考虑使用malloc()和朋友? IOW,VirtualAlloc()是否具有任何真正的优势?
在我看来(可能仅我的观点),malloc()的强大功能和普遍性超过了VirtualAlloc()所承诺的任何吸引力。它会让你更直接地处理你的地区。
很抱歉没有回答。当人们问“谁曾经甚至认为 时,我讨厌它?”当然,当 我 那个问“为什么”的人时会有所不同:-)
答案 2 :(得分:1)
如果要缩小分配,可以在分配的子范围内使用VirtualFree
和MEM_DECOMMIT
。请注意,这不会释放地址空间;只有物理RAM。如果您希望增长它,可以尝试VirtualAlloc
在现有分配后立即传递地址。当然,这可能会失败,此时您需要复制内存。
您还可以尝试将GlobalAlloc
与GMEM_MOVEABLE
和GlobalReAlloc
(或等效的Heap *功能)一起使用。
如果需要释放地址空间,可能需要尝试使用匿名内存映射对象,并在运行时更改其映射窗口 - 或者只是使用64位来获取额外的地址空间。