0

XML ファイルからデータを読み取り、すべての要素 ("< some data/>") をベクター コンテナー vector<TCHAR*>に格納しようとしていますが、タスク マネージャーがベクター サイズよりもはるかに大きいメモリ使用量 (~59mb ではなく ~80mb) を示しているのはなぜですか?

#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

しかし、タスクマネージャーは〜81mbを示しています。私は何を間違っていますか?THNX!

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を実行するまで表示されない可能性が高いのは、メモリフットプリントの違いであり、これは驚くかもしれません。

主な変更点

  • グローバルは、生のwchar_tポインターではなくtokensベクトルになりましたstd::wstring
  • MultiByteToWideChar入力ファイルを翻訳するために使用します。
  • std::wstringスレッドパラメータとして動的に割り当てます。これにより、ファイルイメージの完全なコピーが1つ削除されます。スレッドは、コンテンツの解析が終了deleteした後の責任を負います。wstring
  • _beginthreadex()スレッドを開始するために使用します。これの根本的な理由は、C /C++ランタイムの使用によるものです。以前は、ランタイムは適切にクリーンアップする必要があるさまざまなスレッドローカルストレージを設定していました。これは、を使用する場合に適切_beginthreadex()です。std::threadこれはCreateThread()とほとんど同じですが、正直なところ、MSがそれらをまとめて、他の文明世界のように公式に提供してくれる日を楽しみにしています。

マイナー/無意味な変更

  • グローバル変数は、必要に応じてローカルスコープに移動します。これは、現在、唯一の実際のグローバルがtokensベクトルであることを意味します。
  • tokensスレッドプロシージャは、サブストリングをベクトルに直接プッシュするようになりました。
  • ファイル名にargv[1]を使用します(その方法でデバッグするのは簡単ですが、他の特別な理由はありません)。必要に応じて、ハードコードされたファイル名に戻すことができます。

これにより、これをクリーンアップするためのアイデアが得られることを願っています。さらに重要なことは、与えられたタスクのほぼすべてを、やりくりすることなく、yoyがどのように実行できるかということ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 に答える