1

在过去的一个月里,我一直在为此挠头,但我仍然无法弄清楚发生了什么。

问题是我在使用 Visual Studio 2005 编译的 Windows Server 2008 上运行的 C++ 应用程序上存在非常严重的内存泄漏。这是一个托管项目。该应用程序从大约 5-6MB 开始(根据任务管理器),并在 ~200MB 左右开始出现故障症状。我知道任务管理器是一个粗糙的工具,但考虑到泄漏的规模,它似乎可以使用。

我已将问题缩小到 MySQL 数据库交互。如果应用程序不与数据库交互,则不会泄漏内存。

所有数据库交互都使用 mysql++。我已按照 tangentsoft.net 手册页中的构建说明进行操作。

我们已经评估了代码的线程安全性(也就是说,我们确保每个线程只使用来自该线程的 mysqlpp 对象而不使用其他线程)并检查以确保为使用“new”创建的任何动态生成的对象调用所有析构函数。

在互联网上,我不断看到来自 mysqlpp 类用户的各种报告,这些报告表明某处存在泄漏。特别是,有人讨论了使用 mysqlpp 时 Win C API 会如何泄漏:

http://www.phpmarks.com/6-mysql-plus/ffd713579bbb1c3e.htm

这个讨论似乎在修复中结束,但是,当我在我的应用程序中尝试修复时,它仍然泄漏。

我实现了上面线程中引用的应用程序的一个版本,但添加了一些来自手册页的建议:

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    while (true)
    {
        //Initialise MySQL API
        mysql_library_init(0, NULL, NULL);

        Sleep(50);        
        //Connect to Database.
        mysqlpp::Connection c;        
        c.connect("myDatabase","localhost","username","password");

        Sleep(50);
        //Disconnect from Database
        c.disconnect();

        Sleep(50);
        //Free memory allocated to the heap for this thread
        c.thread_end();

        Sleep(50);
        //Free any memory allocated by MySQL C API
        mysql_library_end();

        Sleep(50);
    }
    return 1;
}

我添加了 Sleep(50) 只是为了限制循环的每个阶段,以便每个函数都有时间“安定下来”。我知道这可能没有必要,但至少这样我可以消除它作为一个原因。

然而,这个程序泄漏得非常快(每小时约 1mb)。

我在几个地方看到过与我类似的问题,但没有得出任何结论:(

所以我并不孤单。我突然想到 mysqlpp 类以有用而闻名,因此必须非常健壮。鉴于这种情况,我仍然看不出我做错了什么。有没有人有一些使用 Visual Studio 2005 的 mysqlpp 经验,可能会阐明这个问题?

干杯,亚当。

编辑

我使用指针创建了另一个示例,以防 c 在循环中被复制:

//LEAKY
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    mysqlpp::Connection * c;

    while (true)
    {
        mysql_library_init(0, NULL, NULL);

        c = new mysqlpp::Connection;

        Sleep(50);

        c->connect("myDatabase","localhost","username","password");

        Sleep(50);
        c->disconnect();

        Sleep(50);
        c->thread_end();

        Sleep(50);
        mysql_library_end();

        Sleep(50);

        delete c;
        c = NULL;
    }       
    return 1;
}

这也泄漏了。然后我根据这段代码创建了一个控制示例,它根本不会泄漏:

//NOT LEAKY
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    char * ch;

    while (true)
    {
        mysql_library_init(0, NULL, NULL);

        //Allocate 4000 bytes
        ch = new char [4000];

        Sleep(250);

        mysql_library_end();

        delete ch;
        ch = NULL;

    }
return 1;
}

请注意,我还在此处留下了对 MySQL C API 的调用,以证明这不是泄漏的原因。然后,我使用指针创建了一个示例,但没有调用连接/断开连接:

//NOT LEAKY
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{

    mysqlpp::Connection * c;

    while (true)
    {
        mysql_library_init(0, NULL, NULL);

        c = new mysqlpp::Connection;
        Sleep(250);

        mysql_library_end();

        delete c;
        c = NULL;
    }

return 1;
}

这不会泄漏。

所以区别只是使用 mysqlpp::connect / disconnect 方法。我将深入研究 mysqlpp 类本身并尝试看看发生了什么。

干杯,亚当。

编辑

这是进行检查的泄漏代码的示例。

//LEAKY
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    mysqlpp::Connection * c;

    while (true)
    {
        mysql_library_init(0, NULL, NULL);

        c = new mysqlpp::Connection;

        Sleep(50);

        if ( c->connect("myDatabase","localhost","username","password") == false )
        {
            cout << "Connection Failure";
            return 0;
        }

        Sleep(50);
        c->disconnect();

        Sleep(50);
        c->thread_end();

        Sleep(50);
        mysql_library_end();

        Sleep(50);

        delete c;
        c = NULL;
    }       
return 1;
}

干杯,亚当。

4

0 回答 0