2011-09-21 100 views
7

我想调整的由MS窗口的VirtualAlloc分配的内存区域。查看VirtualFree文档,可以仅部分解除某个区域的版本,但部分版本无法发布。也就是说,有可能释放部分物理内存,但不是虚拟内存的一部分。如何调整由VirtualAlloc分配的区域的大小?

我知道这可能需要重新分配区域在这种情况下。但是,复制整个区域的效率相当低。有没有办法让windows分配一个不同大小的新区域,指向相同的内存?

+0

虚拟内存的要点是抽象出这些东西,让内核处理内存布局。 –

+0

您的应用程序是否需要释放虚拟内存页面;你用完虚拟地址空间了吗? –

+1

@DanielTrebbien:情况如下..我需要一个新的区域,所以我尝试使用VirtualAlloc进行分配。但是,VirtualAlloc会抛出内存不足。同时,由于我保留自己的一些元数据,我知道某些地区只有一半。我现在想低价缩小或重新分配这些区域,直到我再次记忆。 – cib

回答

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位无符号位字段,其中个如果次得到 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昆明植物研究所的范围一下子,自由,和TH尝试尽可能多地重新保留。

我更新了上面的代码和示例输出。

在多线程环境中,代码可能会回落到第一个预留的“任意位置”分配。也许这是一个好主意,试图保留RESERVATION_SIZE字节,保留 - 然后释放的范围32*RESERVATION_SIZE字节五次左右,最后回落到“任何地方”分配。

+0

有趣的是,我有同样的想法,但我不确定它是否会有效或如何详细完成。实际上,如果我考虑这个问题,甚至不需要位图,因为我只会缩小或扩大“区域”。因此,当前分配页面的计数器就足够了。无论如何,非常感谢代码,太糟糕了,我不能多次投票。 – cib

+0

@cib:我不确定你是否可以通过没有位图获取。例如,可能发生第32次预约无法获得的情况。即使你使用一个互斥锁来保护所有对'VirtualAlloc()'的调用,另一个进程也可以通过['VirtualAllocEx()'](http://msdn.microsoft.com/)在你的进程的地址空间中分配虚拟地址空间。 COM/EN-US /库/ aa366890.aspx)。 –

+0

我认为,一旦我无法提供连续的内存块,我不得不重新分配或引发错误。如果我的“区域”存在漏洞,我不能将其用作单个内存块,而必须将其视为单独的页面集合。另外,虽然这有点偏离主题,但您能否解释为什么其他进程可能需要在我的进程中分配内存?如果没有适当的进程间许可系统,这听起来像是不安全的东西。 – cib

2

不是一个答案,但我要问:

考虑你的痛苦,的VirtualAlloc(),和你的代码的非便携的性能命中;与VIrtualAlloc()给出的任何值相比,你可能会考虑使用malloc()和朋友吗? IOW,VirtualAlloc()是否赋予任何真正的优势?我认为(也许只有我的意见),malloc()的威力和普遍性胜过VirtualAlloc()承诺的任何诱惑。它会让你更直接地处理你的地区。

对不起,我不回答。我讨厌当人们问“谁有有史以来甚至认为要做?”不过,当然这一切都不同,当一问“为什么” :-)

+0

简单,我正在编写我自己的malloc级内存管理器_因为malloc对于我所做的并不高效_ – cib

+0

@cib,在大多数情况下,即使直接使用malloc是低效的,间接使用malloc(即, malloc获得一个大的块,你进一步细分)通常是好的... – bdonlan

+0

@bdonlan:这可能是这样的,但它听起来像一个最好的黑客。使用malloc,你几乎不能控制你分配的位置和方式。例如,我的一些要求可能包括页面对齐和某个地址范围内的地址。当然,一些malloc实现可能会解决,但由于malloc是抽象的,你永远无法确定。 – cib

1

如果要收缩的分配,你可以在分配的子范围使用VirtualFreeMEM_DECOMMIT。请注意,这并不会释放地址空间;只有物理RAM。如果你想扩大它,你可以尝试VirtualAlloc在你现有的分配后立即传递一个地址。当然,这可能会失败,您需要复制内存。

您也可以尝试使用GlobalAllocGMEM_MOVEABLEGlobalReAlloc(或等效的Heap *函数)。

如果你需要释放的地址空间,你可能想使用匿名内存映射对象,并改变他们的映射窗口在运行时尝试 - 或者干脆使用64位来获得额外的地址空间。

+0

没有帮助,他超出了地址空间。VirtualAlloc/Free与需求分页虚拟内存操作系统上的RAM无关。 –

+0

@HansPassant,看我最后的建议 - 他可以使用匿名内存映射对象,并将它们映射出来。或者只是去64位。 – bdonlan

相关问题