2011-09-21 107 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

相關問題