Joh*_*lez 5 service winapi memory-leaks stl visual-studio-2010
我有一个用C++(VS2010)编写的多线程Win32服务,它广泛使用标准模板库.程序的业务逻辑运行正常,但是当查看任务管理器(或资源管理器)时,程序会像筛子一样泄漏内存.
我有一个平均大约16个同时请求/秒的测试集.当程序首次启动时,它会占用1.5mb ram附近的某个地方.完整测试运行(需要12-15分钟)后,内存消耗最终会接近12Mb.通常,这对于运行一次然后终止的程序来说不是问题,但是该程序旨在连续运行.确实非常糟糕.
为了尝试缩小问题范围,我创建了一个非常小的测试应用程序,它以每250ms一次的速率旋转工作线程.工作线程创建一个映射并用伪随机数据填充它,清空映射,然后退出.这个程序也以类似的方式泄漏内存,所以我认为问题在于STL没有按预期释放内存.
我已经尝试过VLD来搜索泄漏,并且已经找到了一些我已经解决的问题,但问题仍然存在.我已经尝试整合Hoard,但这实际上使问题变得更糟(我可能没有正确地集成它,但我看不出如何).
所以我想提出以下问题:是否可以创建一个在多线程环境中使用STL而不会泄漏内存的程序?在过去的一周里,我对这个程序进行了不少于200次的更改.我已经绘制了更改的结果,它们都具有相同的基本配置文件.我不想删除使开发此应用程序变得更容易的所有STL优点.我会真诚地感谢任何有关如何使这个应用程序工作而不泄漏记忆的建议,就像它已经过时了.
再次感谢任何帮助!
PS我发布了一份内存测试副本,用于检查/个人启发.
#include <string>
#include <iostream>
#include <Windows.h>
#include <map>
using namespace std;
#define MAX_THD_COUNT 1000
DWORD WINAPI ClientThread(LPVOID param)
{
unsigned int thdCount = (unsigned int)param;
map<int, string> m;
for (unsigned int x = 0; x < 1000; ++x)
{
string s;
for (unsigned int y = 0; y < (x % (thdCount + 1)); ++y)
{
string z = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
unsigned int zs = z.size();
s += z[(y % zs)];
}
m[x] = s;
}
m.erase(m.begin(), m.end());
ExitThread(0);
return 0;
}
int main(int argc, char ** argv)
{
// wait for start
string inputWait;
cout << "type g and press enter to go: ";
cin >> inputWait;
// spawn many memory-consuming threads
for (unsigned int thdCount = 0; thdCount < MAX_THD_COUNT; ++thdCount)
{
CreateThread(NULL, 0, ClientThread, (LPVOID)thdCount, NULL, NULL);
cout
<< (int)(MAX_THD_COUNT - thdCount)
<< endl;
Sleep(250);
}
// wait for end
cout << "type e and press enter to end: ";
cin >> inputWait;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
使用_beginthreadex()
std 库时使用(就 MS 而言,包括 C 运行时)。此外,您将在 std 运行时子分配器中遇到一定量的碎片,特别是在设计为不断支持像这样越来越大的请求的代码中。
MS 运行时库具有一些函数,允许您调试内存请求,并在您拥有完善的算法并确信您没有看到任何明显的东西后确定是否存在可靠的泄漏。有关详细信息,请参阅调试例程。
最后,我对你写的测试夹具做了如下修改:
希望当您看到输出日志时这会有意义。注意:您必须在调试模式下进行编译,才能为您生成正确的转储。
#include <windows.h>
#include <dbghelp.h>
#include <process.h>
#include <string>
#include <iostream>
#include <map>
#include <vector>
using namespace std;
#define MAX_THD_COUNT 250
#define MAX_THD_LOOPS 250
unsigned int _stdcall ClientThread(void *param)
{
unsigned int thdCount = (unsigned int)param;
map<int, string> m;
for (unsigned int x = 0; x < MAX_THD_LOOPS; ++x)
{
string s;
for (unsigned int y = 0; y < (x % (thdCount + 1)); ++y)
{
string z = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
size_t zs = z.size();
s += z[(y % zs)];
}
m[x].assign(s);
}
return 0;
}
int main(int argc, char ** argv)
{
// setup reporting mode for the debug heap. when the program
// finishes watch the debug output window for any potential
// leaked objects. We're leaking one on purpose to show this
// will catch the leaks.
int flg = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
flg |= _CRTDBG_LEAK_CHECK_DF;
_CrtSetDbgFlag(flg);
static char msg[] = "Leaked memory.";
new std::string(msg);
// will hold our vector of thread handles. we keep this fully populated
// with running threads until we finish the startup list, then wait for
// the last set of threads to expire.
std::vector<HANDLE> thrds;
for (unsigned int thdCount = 0; thdCount < MAX_THD_COUNT; ++thdCount)
{
cout << (int)(MAX_THD_COUNT - thdCount) << endl;
thrds.push_back((HANDLE)_beginthreadex(NULL, 0, ClientThread, (void*)thdCount, 0, NULL));
if (thrds.size() == MAXIMUM_WAIT_OBJECTS)
{
// wait for any single thread to terminate. we'll start another one after,
// cleaning up as we detected terminated threads
DWORD dwRes = WaitForMultipleObjects(thrds.size(), &thrds[0], FALSE, INFINITE);
if (dwRes >= WAIT_OBJECT_0 && dwRes < (WAIT_OBJECT_0 + thrds.size()))
{
DWORD idx = (dwRes - WAIT_OBJECT_0);
CloseHandle(thrds[idx]);
thrds.erase(thrds.begin()+idx, thrds.begin()+idx+1);
}
}
}
// there will be threads left over. need to wait on those too.
if (thrds.size() > 0)
{
WaitForMultipleObjects(thrds.size(), &thrds[0], TRUE, INFINITE);
for (std::vector<HANDLE>::iterator it=thrds.begin(); it != thrds.end(); ++it)
CloseHandle(*it);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出调试窗口
注意:有两起泄漏报告。一个是 std::string 分配,另一个是 std::string 中保存消息副本的缓冲区。
Detected memory leaks!
Dumping objects ->
{80} normal block at 0x008B1CE8, 8 bytes long.
Data: <09 > 30 39 8B 00 00 00 00 00
{79} normal block at 0x008B3930, 32 bytes long.
Data: < Leaked memor> E8 1C 8B 00 4C 65 61 6B 65 64 20 6D 65 6D 6F 72
Object dump complete.
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
670 次 |
最近记录: |