2012-12-02 35 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

如果我将'删除',我如何存储数据? –