从文件读取时内存泄漏

时间:2012-12-02 00:39:09

标签: c++ windows winapi memory memory-leaks

我正在尝试从XML文件中读取数据并将每个元素(“&lt; some data /&gt;”)存储在向量容器vector<TCHAR*>中,为什么任务管理器显示的内存使用量远大于向量大小(~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:〜 fileSize = 57mb vectorSize = 58mb

但是TaskManager显示~81mb。 我究竟做错了什么? 日Thnx!

2 个答案:

答案 0 :(得分:1)

您可以在此do-while循环中分配内存:

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

您永远不会使用delete运算符

发布它

答案 1 :(得分:1)

首先,正如Esthete所指出的那样,一旦你完成它,你永远不会清除令牌向量。这应该完成,或者更改令牌向量以使用自清理内容,如std :: string或std :: wstring。

这让我在旁边并排。请根据您现有的代码进行审核。您需要进行多项更改以进行比较。在cmopile + run之前你可能看不到的是内存占用差异,这可能让你感到惊讶。

重大变化

  • 全局tokens现在是std::wstring的向量,而不是原始的wchar_t指针
  • 使用MultiByteToWideChar翻译输入文件。
  • 动态分配std::wstring作为线程参数。这将删除文件映像的一个完整副本。完成解析内容后,线程负责delete 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;
}