0

我正在尝试从 XML 文件中读取数据并将每个元素(“< some data/>”)存储在矢量容器中 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;
}

输出:~ fileSize = 57mb vectorSize = 58mb

但 TaskManager 显示 ~ 81mb。我究竟做错了什么?太好了!

4

2 回答 2

1

你在这里分配内存,在do-while循环中:

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

而且您永远不会与delete操作员一起发布它

于 2012-12-02T00:43:44.320 回答
1

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

这将我带到了下面并排的位置。请对照您现有的代码查看此内容。您需要比较许多更改。在您 cmopile+run 之前您可能不会看到的是内存占用差异,这可能会让您感到惊讶。

主要变化

  • 全局tokens现在是一个向量std::wstring而不是原始 wchar_t 指针
  • 用于MultiByteToWideChar翻译输入文件。
  • 动态分配一个std::wstring作为线程参数。这将删除文件图像的一份完整副本。线程负责完成对内容deletewstring解析。
  • 用于_beginthreadex()启动线程。其根本原因是因为 C/C++ 运行时使用。过去,运行时设置了必须正确清理的各种线程本地存储,在使用_beginthreadex(). 它几乎与 CreateThread() 相同,但老实说,我期待 MS 将他们的东西放在一起并std::thread像其他文明世界一样正式为我们提供服务的那一天。

微小/无意义的变化

  • 全局变量在适当的地方被带到本地范围。这意味着现在唯一真正的全局是tokens向量。
  • 线程过程现在将子字符串直接推送到tokens向量。
  • 使用 argv[1] 作为文件名(这种方式很容易调试,没有其他特殊原因)。可以根据需要改回您的硬编码文件名。

我希望这能给你一些关于清理这个问题的想法,更重要的是,你如何能够在不发疯new的情况下完成几乎所有的任务。delete

注意:这不会检查输入文件的字节顺序标记。我相信您声称它是 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;
}
于 2012-12-02T06:15:38.263 回答