Visual studio 2010 VS2010上使用STL的多线程Win32服务的内存问题 我有一个用C++编写的多线程Win32服务(VS2010),它广泛地使用了标准模板库。程序的业务逻辑运行正常,但当查看任务管理器(或资源管理器)时,程序会像筛子一样泄漏内存

Visual studio 2010 VS2010上使用STL的多线程Win32服务的内存问题 我有一个用C++编写的多线程Win32服务(VS2010),它广泛地使用了标准模板库。程序的业务逻辑运行正常,但当查看任务管理器(或资源管理器)时,程序会像筛子一样泄漏内存,visual-studio-2010,winapi,service,memory-leaks,stl,Visual Studio 2010,Winapi,Service,Memory Leaks,Stl,我有一个平均每秒16个同时请求的测试集。当程序第一次启动时,它消耗大约1.5Mb内存。在一次完整的测试运行(需要12-15分钟)之后,内存消耗最终接近12Mb。通常情况下,对于运行一次然后终止的程序来说,这不会是一个问题,但该程序旨在连续运行。真的很糟糕 为了缩小问题的范围,我创建了一个非常小的测试应用程序,它以每250ms一次的速度剥离工作线程。工作线程创建一个映射并用伪随机数据填充它,清空映射,然后退出。这个程序也以类似的方式泄漏内存,所以我认为问题在于STL没有按预期释放内存 我尝试了VL

我有一个平均每秒16个同时请求的测试集。当程序第一次启动时,它消耗大约1.5Mb内存。在一次完整的测试运行(需要12-15分钟)之后,内存消耗最终接近12Mb。通常情况下,对于运行一次然后终止的程序来说,这不会是一个问题,但该程序旨在连续运行。真的很糟糕

为了缩小问题的范围,我创建了一个非常小的测试应用程序,它以每250ms一次的速度剥离工作线程。工作线程创建一个映射并用伪随机数据填充它,清空映射,然后退出。这个程序也以类似的方式泄漏内存,所以我认为问题在于STL没有按预期释放内存

我尝试了VLD来寻找漏洞,它已经找到了一对我已经修复的漏洞,但问题仍然存在。我曾尝试集成囤积,但这实际上使问题变得更糟(我可能没有正确地集成它,但我不知道如何集成)

因此,我想提出以下问题:是否有可能创建一个程序,在多线程环境中使用STL,而不会泄漏内存?在过去的一周里,我对这个计划做了不少于200次的修改。我已经绘制了更改的结果,它们都具有相同的基本轮廓。我不想删除STL的所有优点,这些优点使开发这个应用程序变得更加容易。如果您能给我一些建议,告诉我如何让这个应用程序在不泄露内存的情况下正常工作,我将不胜感激

再次感谢您的帮助

另外,我正在张贴一份记忆测试的副本,以供检查/个人熏陶

#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;
}
#包括
#包括
#包括
#包括
使用名称空间std;
#定义最大THU计数1000
DWORD WINAPI客户端线程(LPVOID参数)
{
unsigned int thdCount=(unsigned int)参数;
地图m;
用于(无符号整数x=0;x<1000;++x)
{
字符串s;
对于(无符号整数y=0;y<(x%(thdCount+1));++y)
{
字符串z=“ABCDEFGHIJKLMNOPQRSTUVXYZABCDFGHIJKLMNOPQRSTUVXYZ0123456789”;
无符号整数zs=z.size();
s+=z[(y%zs)];
}
m[x]=s;
}
m、 擦除(m.begin(),m.end());
退出线程(0);
返回0;
}
int main(int argc,字符**argv)
{
//等待开始
字符串输入等待;
cout>输入等待;
//产生许多消耗内存的线程
对于(无符号整数thdCount=0;thdCount\u beginthreadex()
(就MS而言,包括C运行时)。此外,std运行时子分配器中会出现一定数量的碎片,特别是在设计为不断支持越来越大的请求的代码中

MS runtime library有一些函数,允许您调试内存请求,并在拥有可靠的算法并且确信没有看到任何明显的漏洞后确定是否存在可靠泄漏。有关详细信息,请参阅

最后,我对您编写的测试夹具进行了以下修改:

  • 设置正确的_Crt报告模式,以便在关机后向调试窗口发送内存泄漏的垃圾邮件
  • 修改了线程启动循环,以保持在最大等待对象(WIN32当前定义为64个句柄)上持续运行的最大线程数
  • 抛出一个有目的的泄漏字符数组分配,以显示CRT实际上会在程序终止时转储时捕获它
  • 消除了控制台键盘交互。只需运行它
  • 希望当您看到输出日志时,这会有意义。注意:您必须在调试模式下进行编译,以便为您进行适当的转储

    #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;
    }
    
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    #包括
    使用名称空间std;
    #定义最大THU计数250
    #定义最大THD循环250
    无符号int_stdcall ClientThread(void*param)
    {
    unsigned int thdCount=(unsigned int)参数;
    地图m;
    for(无符号整数x=0;x调试大型应用程序并非易事。
    您的样本不是显示正在发生的情况的最佳选择。
    真实代码的一个片段猜测得更好。
    当然这是不可能的,所以我的建议是:使用尽可能多的日志,包括中的插入和删除控件
    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.