私はこの1か月間これに頭を悩ませてきましたが、何が起こっているのかまだ理解できません。
問題は、VisualStudio2005を使用してコンパイルされたWindowsServer2008で実行されているC++アプリケーションで非常に深刻なメモリリークが発生することです。これは管理対象プロジェクトです。アプリケーションは(タスクマネージャーによると)約5〜6 MBで開始し、約200MBのマーク付近で障害の兆候を示し始めます。タスクマネージャーが大雑把なツールであることは知っていますが、リークの規模を考えると、使用しても問題ないようです。
問題をMySQLデータベースの相互作用に絞り込みました。アプリケーションがデータベースと対話しない場合、メモリはリークされません。
すべてのデータベースの相互作用はmysql++を使用します。私はtangentsoft.netのmanページにあるビルド手順に従いました。
スレッドセーフについてコードを評価し(つまり、各スレッドがそのスレッドのmysqlppオブジェクトのみを使用し、他のスレッドは使用しないことを確認しました)、「new」を使用して作成された動的に生成されたオブジェクトに対してすべてのデストラクタが呼び出されることを確認しました。
インターネットを見ると、どこかにリークがあることを示すmysqlppクラスのユーザーからのさまざまなレポートが表示され続けています。特に、mysqlppを使用したときにWinCAPIがどのようにリークするかについての議論がありました。
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)を追加して、各関数が「落ち着く」時間を確保できるようにしました。おそらく必要ないことはわかっていますが、少なくともこの方法で、原因としてそれを排除することができます。
それにもかかわらず、このプログラムは非常に急速にリークします(1時間あたり約1MB)。
私はいくつかの場所で私と同様の質問をしましたが、結論は出ていません:(
だから私はこの問題だけではありません。mysqlppクラスは有用性に定評があるため、非常に堅牢である必要があります。それが事実であることを考えると、私はまだ私が間違ったことを見ることができません。問題に光を当てる可能性のあるVisualStudio2005での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;
}
乾杯、アダム。