2012-12-02 30 views
0

爲什麼任務管理器顯示的內存使用量遠遠大於向量大小,爲什麼要從矢量容器vector<TCHAR*>中讀取XML文件中的數據並存儲每個元素(「< some data />」) (〜代替80MB〜59MB):從文件讀取時發生內存泄露

#define _UNICODE 

#include<tchar.h> 
#include<iostream> 
#include<windows.h> 
#include<vector> 

using namespace std; 

HANDLE hFile; 
HANDLE hThread; 
vector<TCHAR*> tokens; 
DWORD tokensSize; 

DWORD WINAPI Thread(LPVOID lpVoid); 


void main() 
{ 
    tokensSize = 0; 
    hFile = CreateFile("db.xml",GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); 
    if(hFile == INVALID_HANDLE_VALUE) { 
     cout<<"CreateFile Error # "<<GetLastError()<<endl;  
    } 

    DWORD fileSize = GetFileSize(hFile,NULL); 
    cout<<"fileSize = "<<fileSize<<" bytes = "<<fileSize/1024/1024<<" mb"<<endl; 
    TCHAR* buffer = new TCHAR[fileSize/sizeof(TCHAR) + 1]; 
    ZeroMemory(buffer,fileSize); 

    DWORD bytesRead; 
    if(!ReadFile(hFile,buffer,fileSize,&bytesRead,NULL)){ 
     cout<<"ReadFile Error # "<<GetLastError()<<endl;   
    } 
    CloseHandle(hFile); 

    hThread = CreateThread(NULL,0,Thread,(LPVOID)buffer,0,NULL);  

    WaitForSingleObject(hThread,INFINITE); 

    for(int i=0;i<tokens.size();i++) 
      tokensSize+=(_tcslen(tokens[i])+1)*sizeof(TCHAR); 
    cout<<"vector size = "<<tokensSize<<" bytes = "<<tokensSize/1024/1024<<" mb"<<endl; 
    cin.get(); 
} 

DWORD WINAPI Thread(LPVOID lpVoid) 
{ 
    wstring entireDB = (TCHAR*)lpVoid; 
    delete[]lpVoid; 

    wstring currentElement; 
    wstring::size_type lastPos = 0; 
    wstring::size_type next; 

    next = entireDB.find(_T(">"),lastPos); 
    TCHAR* szStr; 
    do 
    {    
     currentElement = entireDB.substr(lastPos,next+1-lastPos); 
     szStr = new TCHAR[currentElement.length()+1]; 
     _tcscpy(szStr,currentElement.c_str()); 
     tokens.push_back(szStr); 
     lastPos = next+1; 
     next = entireDB.find(_T(">"),lastPos); 
    } 
    while(next != wstring::npos); 

    entireDB.clear(); 
    return 0; 
} 

OUTPUT:〜 檔案大小= 57MB vectorSize = 58MB

但任務管理顯示〜81MB。 我在做什麼錯? THNX!

+0

Mickey,這個文件是UTF8 XML嗎?我問,因爲它看起來像TCHAR,但它可以是'char'或'wchar_t',具體取決於compilaton。這段代碼有很多錯誤,但我想在發佈響應之前知道更多的東西(這是其中之一)。另外,您是使用Visual Studio構建的,還是使用MinGW,裸露的Windows SDK或什麼?謝謝。 – WhozCraig

+0

嗨。其UTF-8,VisualStudio和裸WindowsSDK.Thank你! –

回答

1

首先,唯美主義者釋放它,你永遠不清除標記向量,一旦你'完成了它。這應該完成,或更改令牌向量以使用自清潔內容,如std :: string或std :: wstring。

這將我帶到下面。請根據您現有的代碼進行檢查。您需要比較一些更改。在你cmopile +運行之前你可能看不到的是內存佔用差異,這可能會讓你感到驚訝。

重大變化

  • 全球tokens現在的std::wstring一個矢量而不是原始wchar_t的指針
  • 用途MultiByteToWideChar翻譯輸入文件。
  • 動態分配std::wstring作爲線程參數。這將刪除文件映像的一個完整副本。一旦完成內容解析,該線程負責wstring。使用_beginthreadex()來啓動線程。其根本原因是由於C/C++運行時的使用情況。在過去,運行時會設置必須正確清理的各種線程本地存儲,並且在使用_beginthreadex()時也是如此。它幾乎與CreateThread()完全相同,但說實話,我期待着MS把他們的東西放在一起的那一天,並正式給我們std::thread,就像其他文明世界一樣。

次要/無意義的變化

  • 全局變量被帶到當地的範圍適當。這意味着現在唯一真正的全球是tokens載體。
  • 線程過程現在將子字符串直接推向tokens向量。
  • 使用argv [1]作爲文件名(易於調試,沒有其他特殊原因)。可以根據需要更改回硬編碼的文件名。

我希望這給你清理這件事的一些想法,更重要的是,同比如何能做到幾乎全部的任務,你不必再newdelete堅果給出。

備註:這不檢查輸入文件的字節順序標記。我相信,你聲稱它是UTF8是直接的,並且在文件開始時沒有BOM。如果你的輸入文件有BOM,你需要調整讀取文件的代碼來解決這個問題。

#include <windows.h> 
#include <tchar.h> 
#include <process.h> 
#include <iostream> 
#include <vector> 
#include <string> 
using namespace std; 

// global map of tokens 
vector<wstring> tokens; 

// format required by _beginthreadex() 
unsigned int _stdcall ThreadProc(void *p); 

int main(int argc, char *argv[]) 
{ 
    HANDLE hThread = NULL; 
    std::string xml; 
    std::wstring* pwstr = NULL; 

    // check early exit 
    if (argc != 2) 
    { 
     cout << "Usage: " << argv[0] << " filename" << endl; 
     return EXIT_FAILURE; 
    } 

    // use runtime library for reading the file content. the WIN32 CreateFile 
    // API is required for some things, but not for general file ops. 
    HANDLE hFile = CreateFileA(argv[1], GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL); 
    if (hFile != INVALID_HANDLE_VALUE) 
    { 
     DWORD dwFileSize = GetFileSize(hFile, NULL); 
     if (dwFileSize > 0) 
     { 
      // allocate a string large enough for the whole file. 
      std::string xml(dwFileSize, 0); 
      DWORD bytesRead = 0; 
      if (ReadFile(hFile, &xml.at(0), dwFileSize, &bytesRead, NULL) && (bytesRead == dwFileSize)) 
      { 
       // invoke MB2WC to determine wide-char requirements 
       int ires = MultiByteToWideChar(CP_UTF8, 0, xml.c_str(), -1, NULL, 0); 
       if (ires > 0) 
       { 
        // allocate a wstring for our thread parameter. 
        pwstr = new wstring(ires, 0); 
        MultiByteToWideChar(CP_UTF8, 0, xml.c_str(), -1, &pwstr->at(0), ires); 

        // launch thread. it own the wstring we're sending, including cleanup. 
        hThread = (HANDLE)_beginthreadex(NULL, 0, ThreadProc, pwstr, 0, NULL); 
       } 
      } 
     } 

     // release the file handle 
     CloseHandle(hFile); 
    } 

    // wait for potential thread 
    if (hThread != NULL) 
    { 
     WaitForSingleObject(hThread, INFINITE); 
     CloseHandle(hThread); 
    } 

    // report space taken by tokens 
    size_t tokensSize = 0; 
    for (vector<wstring>::const_iterator it = tokens.begin(); it != tokens.end(); ++it) 
     tokensSize += it->size()+1; 
    cout << "tokens count = " << tokens.size() << endl 
     << "tokens size = "<< tokensSize <<" bytes" << endl; 

    cin.get(); 
} 

// our thread parameter is a dynamic-allocated wstring. 
unsigned int _stdcall ThreadProc(void *p) 
{ 
    // early exit on null insertion 
    if (p == NULL) 
     return EXIT_FAILURE; 

    // use string passed to us. 
    wstring* pEntireDB = static_cast<wstring*>(p); 
    wstring::size_type last = 0; 
    wstring::size_type next = pEntireDB->find(L'>',last); 
    while(next != wstring::npos) 
    {    
     tokens.push_back(pEntireDB->substr(last, next-last+1)); 
     last = next+1; 
     next = pEntireDB->find(L'>', last); 
    } 

    // delete the wstring (no longer needed) 
    delete pEntireDB; 

    return EXIT_SUCCESS; 
} 
1

你在這裏分配內存,在do-while循環:

szStr = new TCHAR[currentElement.length()+1]; 

正如指出的,你永遠不會與delete操作

+0

如果我將'刪除',我如何存儲數據? –