メモリーリークとは、プログラムで使用するために割り当てられているが、プログラムのデータ領域中のいずれも指していないポインタを持つ、動的に割り当てられたメモリーブロックを言います。そのようなブロックは、ブロックを指しているポインタがないため、プログラムはそれらのブロックを参照できず、ましてや解放することもできません。RTC はこのようなブロックを検知し、報告します。
メモリーリークは仮想メモリーの使用を増やし、一般的にメモリーの断片化を招きます。その結果、プログラムやシステム全体のパフォーマンスが低下する可能性があります。
メモリーリークは、通常、割り当てメモリーを解放しないで、割り当てブロックへのポインタを失うと発生します。メモリーリークの例を次に示します。
void foo() { char *s; s = (char *) malloc(32); strcpy(s, "hello world"); return; /* no free of s. Once foo returns, there is no */ /* pointer pointing to the malloc’ed block, */ /* so that block is leaked. */ }
リークは、API の不正な使用が原因で起こる可能性があります。
void printcwd() { printf("cwd = %s\n", getcwd(NULL, MAXPATHLEN)); return; /* libc function getcwd() returns a pointer to */ /* malloc’ed area when the first argument is NULL, */ /* program should remember to free this. In this */ /* case the block is not freed and results in leak.*/ }
メモリーリークを防ぐには、必要のないメモリーは必ず解放します。また、メモリーを確保するライブラリ関数を使用する場合は、メモリーを解放することを忘れないでください。
解放されていないブロックを「メモリーリーク」と呼ぶこともあります。プログラムがすぐに終了する場合にメモリーを解放しないことは一般的なプログラミング習慣であるため、この定義はほとんど役に立ちません。プログラムがブロックへのポインタを引き続き 1 つ以上保持している場合、実行時検査ではそのブロックをリークとして報告しません。
mel – メモリーリーク (mel) エラーを参照
air – レジスタ中のアドレス (air)を参照
aib – ブロック中のアドレス (aib)を参照
実行時検査では、「起こり得る」リークを 2 つのケースで報告できます。最初のケースは、ブロックの先頭を指しているポインタは見つからないが、ブロックの内部を指しているポインタが見つかった場合です。このケースは、「ブロック中のアドレス」(aib) エラーとして報告されます。ブロックの内部を指している浮遊ポインタが実際のメモリーリークになります。ただし、プログラムによってはポインタに対して故意にそのような動作をさせている場合があり、このケースはメモリーリークになりません。RTC はこの違いを判別できないため、本当にリークが発生しているかどうかはユーザー自身の判断で行う必要があります。
2 つ目のタイプの起こり得るリークは、データ領域内にはブロックへのポインタが見つからないが、レジスタ内にポインタが見つかった場合に発生します。このケースは、「レジスタ中のアドレス」(air) エラーとして報告されます。レジスタが誤ってブロックを指している場合や、それが以前に失われたメモリーポインタの古いコピーである場合、これは実際のリークです。ただし、コンパイラが最適化のために、ポインタをメモリーに書き込むことなく、レジスタのブロックに対して参照させることがありますが、この場合はメモリーリークではありません。そのため、プログラムが最適化されており、そのレポートが showleaks コマンドの結果であった場合、それは実際のリークでない可能性があります。 リークでない可能性があります。詳細については、showleaks コマンドを参照してください。
メモリーリーク検査が有効になっている場合、メモリーリークのためのスキャンは、テスト対象のプログラムが終了する直前に自動的に実行されます。検出されたリークはすべて報告されます。このプログラムを、kill コマンドで強制終了してはいけません。 次の例は、メモリーリークエラーの標準的なメッセージです。
Memory leak (mel): Found leaked block of size 6 at address 0x21718 At time of allocation, the call stack was: [1] foo() at line 63 in test.c [2] main() at line 47 in test.c
UNIX プログラムには、そのプログラムの最上位のユーザー関数である main 手続き (f77 では MAIN と呼ばれます) が含まれています。プログラムは exit(3) が呼び出されるか、main から返った時点で終了します。後者の場合は、戻ったあとに main にローカルなすべての変数がスコープから外れるため、大域変数がこれらの同じブロックを指していないかぎり、それらの変数が指していたヒープブロックはすべてリークとして報告されます。
プログラムは終了しようとしており、exit() を呼び出すことなく main から復帰するため、一般的なプログラミング習慣では、main で局所変数に割り当てられたヒープブロックを解放しません。これらのブロックがメモリーリークとして報告されないようにするには、main 内の最後の実行可能なソース行にブレークポイントを設定することによって、main から復帰する直前でプログラムを停止します。プログラムがそこで停止したとき、RTC の showleaks コマンドを実行すれば、main() とそこで呼び出されるすべての手続きで参照されなくなったヒープブロックのすべてが表示されます。
詳細については、showleaks コマンドを参照してください。
リーク検査が有効になっている場合は、プログラムが終了したときに自動リークレポートを受信します。kill コマンドでプログラムを終了した場合を除き、リークの可能性がすべて報告されます。レポート内の詳細レベルは、dbxenv 変数 rtc_mel_at_exit によって制御されます。デフォルトでは、簡易リークレポートが生成されます。
レポートは、リークのサイズによってソートされます。実際のメモリーリークが最初に報告され、次に可能性のあるリークが報告されます。詳細レポートには、スタックトレース情報の詳細が示されます。行番号とソースファイルが使用可能であれば、これらも必ず含まれます。
次のメモリーリークエラー情報が、2 種類の報告のどちらにも含まれます。
リークのあるブロックのサイズ
リークのあるブロックが割り当てられていた場所
リークのあるブロックのアドレス
check -frames によって制約される、割り当て時の呼び出しスタック
次に、対応する簡易メモリーリークレポートを示します。
Actual leaks report (actual leaks: 3 total size: 2427 bytes) Total Num of Leaked Allocation call stack Size Blocks Block Address ========== ====== ========== ======================================= 1852 2 - true_leak < true_leak 575 1 0x22150 true_leak < main Possible leaks report (possible leaks: 1 total size: 8 bytes) Total Num of Leaked Allocation call stack Size Blocks Block Address ========== ====== ========== ======================================= 8 1 0x219b0 in_block < main
次の例は、標準的な詳細リークレポートを示しています。
Actual leaks report (actual leaks: 3 total size: 2427 bytes) Memory Leak (mel): Found 2 leaked blocks with total size 1852 bytes At time of each allocation, the call stack was: [1] true_leak() at line 220 in "leaks.c" [2] true_leak() at line 224 in "leaks.c" Memory Leak (mel): Found leaked block of size 575 bytes at address 0x22150 At time of allocation, the call stack was: [1] true_leak() at line 220 in "leaks.c" [2] main() at line 87 in "leaks.c" Possible leaks report (possible leaks: 1 total size: 8 bytes) Possible memory leak -- address in block (aib): Found leaked block of size 8 bytes at address 0x219b0 At time of allocation, the call stack was: [1] in_block() at line 177 in "leaks.c" [2] main() at line 100 in "leaks.c"
showleaks コマンドを使用すると、いつでもリークレポートを要求できます。このコマンドは、最後の showleaks コマンド以降の新しいメモリーリークを報告します。 詳細については、showleaks コマンドを参照してください。
リークレポートの数が多くなるのを避けるため、RTC は同じ場所で割り当てられたリークを自動的に 1 つにまとめて報告します。リークを結合するか、またはリークを個別に報告するかの決定は、check –leaks の -match m オプション、または showleaks コマンドの -m オプションで指定される number-of-frames-to-match パラメータによって制御されます。呼び出しスタックが 2 つ以上のリークを割り当てる際に m 個のフレームと一致した場合は、リークは 1 つにまとめて報告されます。
次の 3 つの呼び出しシーケンスを考えてみます。
|
これらのブロックがすべてメモリーリークを起こす場合、m の値によって、これらのリークを別々に報告するか、1 つのリークが繰り返されたものとして報告するかが決まります。m が 2 のとき、ブロック 1 とブロック 2 のリークは 1 つのリークが繰り返されたものとして報告されます。これは、malloc() の上にある 2 つのフレームが共通しているためです。ブロック 3 のリークは、c() のトレースがほかのブロックと一致しないので別々に報告されます。m が 2 より大きい場合、実行時検査では、すべてのリークを個別のリークとして報告します。malloc は、リークレポートには示されません。
一般に、m の値が小さければリークのレポートもまとめられ、m の値が大きければまとめられたリークレポートが減り、別々のリークレポートが生成されます。
メモリーリークレポートを取得したら、メモリーリークを修正するための次のガイドラインに従ってください。
リークの修正でもっとも重要なことは、リークがどこで発生したかを判断することです。作成されるリーク報告は、リークが発生したブロックの割り当てトレースを示します。リークが発生したブロックは、ここから割り当てられたことになります。
次に、プログラムの実行フローを見て、どのようにそのブロックを使用したかを調べます。ポインタが失われた箇所が明らかな場合は簡単ですが、それ以外の場合は showleaks コマンドを使用してリークの検索範囲を狭くすることができます。デフォルトでは、showleaks コマンドは、最後の showleaks コマンド以降に作成された新しいリークのみを一覧表示します。 showleaks を繰り返し実行することにより、ブロックがリークを起こした可能性のある範囲が狭まります。
詳細については、showleaks コマンドを参照してください。