gpt4 book ai didi

c++ - 从文件读取时内存泄漏

转载 作者:行者123 更新时间:2023-11-28 03:26:56 25 4
gpt4 key购买 nike

我正在尝试从 XML 文件中读取数据并将每个元素(“<一些数据/>”)存储在 vector 容器中 vector<TCHAR*> ,为什么任务管理器显示内存使用量远大于 vector 大小(~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;
}

输出:~文件大小 = 57mb vector 大小 = 58mb

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

最佳答案

首先,正如 Esthete 所指出的那样,一旦完成标记 vector ,就永远不会清除它。这应该完成,或者更改 token vector 以利用 std::string 或 std::wstring 等自清理内容。

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

主要变化

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

次要/无意义的更改

  • 在适当的时候将全局变量引入本地范围。这意味着现在唯一真正的全局是 tokens vector 。
  • 线程过程现在将子字符串直接推送到 tokens vector 。
  • 使用 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;
}

关于c++ - 从文件读取时内存泄漏,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13665290/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com