visual-studio-2010 - 在 VS2010 上使用 STL 的多线程 Win32 服务的内存问题

标签 visual-studio-2010 winapi service memory-leaks stl

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

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

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

我尝试过 VLD 来搜索泄漏,它发现了一些我已经修复的问题,但问题仍然存在。我曾尝试集成 Hoard,但这实际上使问题变得更糟(我可能没有正确集成它,但我看不出是如何集成的)。

所以我想提出以下问题:是否可以创建一个在不会泄漏内存的多线程环境中使用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 库时使用 _beginthreadex()(就 MS 而言,包括 C 运行时)。此外,您将在 std 运行时子分配器中遇到一定数量的碎片,特别是在旨在不断支持越来越大的请求的代码中。

MS 运行时库具有一些函数,可让您调试内存请求,并在您拥有健全的算法并确信您没有看到任何明显明显的情况后确定是否存在可靠泄漏。有关更多信息,请参阅 the debug routines

最后,我对你写的测试治具做了如下修改:

  • 设置适当的 _Crt 报告模式,以便在关闭后向调试窗口发送任何内存泄漏。
  • 修改线程启动循环以保持最大线程数不断运行在 MAXIMUM_WAIT_OBJECTS(WIN32 当前定义为 64 个句柄)
  • 加入一个有意泄露的字符数组分配,以显示 CRT 实际上会在程序终止时转储时捕获它。
  • 消除了控制台键盘交互。运行它。

  • 希望当您看到输出日志时这会有意义。注意:您必须在 Debug模式下编译才能为您制作任何适当的转储。
    #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::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.
    

    关于visual-studio-2010 - 在 VS2010 上使用 STL 的多线程 Win32 服务的内存问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13733537/

    相关文章:

    android - 如何停止定时器 Android

    visual-studio-2010 - VS2010 - 打包/发布 Web - 选项已禁用

    c# - 如何以编程方式检测代码是否在共享 DLL 或 exe 中运行?

    tomcat - Tomcat 上的 Axis2

    wpf - 确保 WPF 窗口始终位于顶部,即使用户单击另一个最大化的应用程序也是如此

    windows - 我们如何在delphi中获取键盘空闲时间

    service - 我可以使用 Fiddler 一次发送多个请求吗?

    visual-studio-2010 - Visual Studio 2010 书签问题

    mysql - 备份问题

    visual-studio-2010 - VS 2010 SP1如何选择boost lib文件的版本?