2010-08-18 66 views
1

我在Windows Vista及以上版本中遇到過一個奇怪的問題。當我用大堆棧使用IFileOpenDialog時,顯示對話框後剩餘的內存量下降了大約1 GB。Windows Vista +內存IFileOpenDialog和大堆棧的問題

#include <windows.h> 
#include <Shobjidl.h> 
#include <iostream> 

void memGrab(char *); 
int main(int argc, char **argv){ 
    char Message[128]; 
    CoInitialize(NULL); 
    if(argc > 1){ 
    wsprintf(Message, "Starting Memory Test: %c", argv[1][0]); 
    std::cout << Message << "\n"; 
    if(argv[1][0] == 'b') 
     memGrab("before"); 
    TCHAR *fileName = (TCHAR *)malloc(sizeof(TCHAR) * 1024); 

     IFileOpenDialog *pfd; 
    int index = 0; 
    int len = 0; 
    int i = 0; 

    HRESULT hr = CoCreateInstance(CLSID_FileOpenDialog, 
      NULL, 
      CLSCTX_INPROC_SERVER, 
      IID_PPV_ARGS(&pfd)); 

    if(SUCCEEDED(hr)){ 
     pfd->Show(NULL); 
    } 
    pfd->Release(); 
    if(argv[1][0] == 'a') 
     memGrab("After"); 
    } 
    CoUninitialize(); 
} 

void memGrab(char *text){ 
    for(int i = 0; i < 400000; i++){ 
    if(!malloc(10240)){ 
     char Message[128]; 
     wsprintf(Message, "Memory Gone %s: %d K", text, i * 10); 
     std::cout << Message << "\n"; 
     exit(0); 
    } 
    } 
} 

當我編譯該程序而不設置堆棧大小(cl testMem.cpp Shell32.lib Ole32.lib user32.lib)。我得到以下結果:

C:\RWSource>testMem.exe b 
Starting Memory Test: b 
Memory Gone before: 1984040 K 

C:\RWSource>testMem.exe a 
Starting Memory Test: a 
Memory Gone After: 1875640 K 

但是,當我設置堆棧大小(cl testMem.cpp Shell32.lib Ole32.lib user32.lib /F100000000)編譯它。我失去了大量的可分配內存。

C:\RWSource>testMem.exe b 
Starting Memory Test: b 
Memory Gone before: 1795370 K 

C:\RWSource>testMem.exe a 
Starting Memory Test: a 
Memory Gone After: 463840 K 

更新1:

我檢查用的VMMap內存(謝謝TheUndeadFish),當我打開的對話框中,有多個線程啓動的所有有關於100MB。有沒有辦法讓主線程有一個大的堆棧,而不是給每個線程100MB的線程?

更新2:

幾個月後我再次回到這個問題。我將主代碼移至另一個函數,並將其作爲具有指定堆棧大小的線程啓動。編譯時不需要堆棧大小。

int main(){ 
    CreateThread(NULL, 100000000, analyze, NULL, 0, NULL); 
    // Wait for analyze thread. 
} 

int analyze(int ignore){ 
    // Do stuff 
} 
+0

爲什麼要將堆棧大小設置爲100 MB?對話框COM對象是否有可能產生自己的工作線程,這也可以獲得100 MB的堆棧?嘗試在使用Process Explorer或VMMap之前和之後查看它。 – Luke 2010-08-18 23:35:34

+0

這就是發生了什麼,我如何讓所有的線程都達到100MB? – zmbush 2010-08-19 00:07:05

+0

我不認爲你可以。如果調用者未明確指定堆棧大小(這很少完成),則CreateThread使用鏈接器在可執行文件中指定的堆棧大小。我能想到的唯一的事情就是使用默認值併產生自己的線程來指定所需的堆棧大小。 – Luke 2010-08-19 03:16:23

回答

1

我不知道你的情況到底發生了什麼。但是我看到文件打開對話框會導致其他內存故障。

當該對話框被調用時,很多東西可能會被加載到你的過程中。特別是,Windows資源管理器中出現的外殼擴展也可以使用文件打開對話框加載。這就是你能夠在該對話框中右鍵單擊文件並獲得所有這些非標準的好東西的方式。但是,這意味着這些擴展的基礎DLL正在加載到您的進程中。

根據經驗,我發現一旦文件打開對話框關閉,其中的一些DLL就不會被卸載。相反,他們保持現狀,佔用你的流程的一些地址空間。 (也許這是這些dll中的某種bug,或者可能是某種「優化」,以便在再次使用文件打開對話框的情況下使它們保持加載狀態。)

這樣我發出的結果而且它影響了單個1GB內存分配的成功。在打開對話框之前,有大量的地址空間可用,1GB分配可能成功。然而,在打開對話框之後,一些shell擴展DLL正好位於可用地址空間的中間,並將其分成小於1GB的塊,從而導致分配失敗。

我知道這個軼事並不直接與你在做什麼,因爲你分配的是很多小數量而不是一個大數額。 (在我的場景中,它不是使用malloc,我認爲它就像VirtualAlloc或類似的東西)。但我猜想可能會發生類似的事情。

文件打開對話框可能會在您的進程中加載​​某種東西,以某種方式將障礙插入malloc通常使用的空間中。但不知何故,只有當你使用這麼大的堆棧大小時,纔會發生這種情況,因爲我猜想爲堆棧預留的額外99MB空間會以某種方式重新排列其餘的地址空間,使其易受此問題影響。

當我調查我自己的問題時,我用VMMap在各個點上拍攝了我的進程地址空間的快照,以瞭解發生了什麼。你可能有這樣的運氣。請注意,它會像操作系統看到的那樣查看地址空間,並不像您從上下文中看到特定的語言,如C++。因此,找出malloc或任何其他分配機制正在使用的內存的特定部分並不一定容易。

+0

我將把你的標記作爲答案,VMMap幫助我診斷問題。 (IFileOpenDialog打開了11個線程,每個線程都有100MB的堆棧) – zmbush 2010-08-20 16:43:55