MFC のダイアログでメモリリークが発生する

 

Visual C++ でMFC のダイアログ形式のアプリケーションを長時間動作させている場合、メモリ使用量が増え続ける可能性があります。

 

なぜダイアログ形式の場合にメモリ使用量が増えるのか

 

このメモリ使用量の増加は、MFC がウィンドウなどの管理のために内部で生成している一時オブジェクトに紐づいています。

MFCアプリケーションの形式のうち、SDI および MDI アプリケーションでは、メッセージ ループの空き時間に、CWinApp::OnIdle() 関数が呼び出されて、一時的なオブジェクトを自動的に削除しています。

<SDI および MDI アプリケーションの場合 >

AfxWinMain() image002

CWinApp::InitInstance()

image002[1] 

CWinApp::Run() ===>  この 実行によりメッセージ ループの処理に入り、メッセージ ループの空き時間に一時的なオブジェクトを削除します

一方、MFC のダイアログ形式のアプリケーションでは上記 SDI ないし MDI アプリケーションのように、Run() 関数やその中で呼び出される OnIdle() 関数が呼び出されることなくダイアログが終了します。そのため、一時オブジェクトが破棄されずに残ります。

<ダイアログ形式のアプリケーションの場合>

AfxWinMain() image002[2] 

CWinApp::InitInstance() image002[3] 

メイン ダイアログの生成 ===> Run() 呼び出しのように OnIdle() 関数を呼び出す処理がありません image002[4]

 子ダイアログなどの生成 (あわせて一時オブジェクトが生成されます) image002[5] 

子ダイアログが破棄されると処理はメイン ダイアログのメッセージ ループに戻りますが、CWinApp::Run() の場合と異なり、一時オブジェクトは破棄されずに残っています

 

これはMFCのダイアログ形式の場合には、想定された動作となります。

 

 

解消方法

アプリケーション中で定期的に CWinApp::OnIdle 関数を呼ぶことで、一時的なオブジェクトの削除を行います。

下記の記述を、プログラム中の定期的に呼び出される場所に追加します。OnIdle の引数には 1 を指定します。

AfxGetApp()->OnIdle(1);

OnIdle関数の詳細はMSDN のドキュメントをご参考ください。

-CWinApp::OnIdle https://msdn.microsoft.com/ja-jp/library/3e077sxt.aspx

 

 

 

メモリの増加(リーク)の確認方法

OS 付属のパフォーマンス モニターを使用して、メモリ使用量が視覚的に確認できます。

-手順

Windows 8 の場合:

1. Windows キー+ R を同時に押し、「ファイル名を指定して実行」画面を表示します。

2.  "perfmon” と入力し、パフォーマンス モニターを起動します。

3. パフォーマンス - パフォーマンス モニターをクリックします。

4. 緑十字の追加ボタンを押下し、カウンターを追加ダイアログを表示します。

 

 

5. 追加するカウンターを選択し、「追加」- 「OK」ボタンを押下します。

アプリケーション(プロセス)のメモリ使用量を見る場合は、下記の値を選択します。

上部のボックスでは、Process → PrivateBytes を選択します。

選択したオブジェクトのインスタンスボックスでは、対象のアプリケーションのexe 名を選択します。

 

 

下のMFCダイアログベースのサンプルプログラム(mfc002.exe)では、徐々にPrivateBytesの量が増加し減らないことを確認できます。

 

 

比較として、mfc002.exe にOnIdle関数を実装した場合のメモリ使用量の推移は下記となります。

使用量がほぼ一定であることが確認できます。

 

 

それでもメモリリークが発生する場合

OnIdle関数を実装した後もメモリリークが発生する場合は、再現サンプルプログラムなどをご準備いただき、弊社サポートまでお問い合わせください。

なお、ご質問の内容によっては、サポート対応が難しい場合や、アドバイザリー サービス をお申し込みいただく必要がある場合もございますが、まずは一度ご相談いただければ幸いです。