在过去的一个月里,我一直在为此挠头,但我仍然无法弄清楚发生了什么。
问题是我在使用 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;
}
干杯,亚当。