現代のプログラミングに於いてメモリの動的割り当ては欠かせない機能の一つである。
リソースは「要る物を」「要る時に」「要る分だけ」無駄なく割り当てることは重要だと考える。
近代的な言語にはガベージコレクタを標準で備えるようになっているが、言語 Cでのメモリ管理はプログラマに任せられている。
我々は動的メモリ管理のために mallocや free関数を使用するが、どうしても間違いが生じてしまう。
- 未割り当て領域へのアクセス
- 使用中の領域を解放/解放済み領域へのアクセス
- 二重解放
- 解放漏れ(メモリリーク)
- オーバラン/アンダーラン
softiesプロジェクトでは、これらの問題を検出するための LWD mallocライブラリを開発している。
この用途には valgrindというメモリの監視以外にも使える使いやすいツールが既にあるが、Solarisには非対応で、動作も若干鈍調である。
我々はより軽量で Solarisでも使用できることを目標に置いている。
使用法
このライブラリはメモリを大量に消費する。そのため、専ら小規模な単体テストに使用されることを想定している。
静的ライブラリ libmalloc.aをテスト対象のプログラムにリンクして使用する。
(都合が悪ければライブラリ名称は任意に変更して良い)
libmalloc.aがホームディレクトリの下のlibにあり、Test.cからデバッグ版の Testをビルドする例を以下に示す。
$ cc -g -o Test -L ~/lib -lmalloc Test.c
Makefileを使用する場合には、下記の定義を含めればよい。
CFLAGS = -g
LFLAGS = -L ~/lib
LDLIBS = -lmalloc
正常な使用例
まず、メモリを割り当て、それを解放した場合の例を見てみよう。#include <malloc.h>
#include <stdio.h>
int main() {
char* bytes1 = (char*)malloc(5);
char* bytes2 = (char*)malloc(8);
free(bytes1);
free(bytes2);
return 0;
}
このプログラムは 5バイトと 8バイトの領域を割り当て、その両方を解放する Test1.cである。
$ ./Test1
HEAP REPORT
SUMMARY
total allocated: 13(bytes) 2
memory leak: 0(bytes) 0
SUMMARYには、total allocatedに 2回の割り当てで合計 13バイト割り当て、memory leakに解放漏れが無いことが示されている。
(bytes)の後ろの値は領域の数を表している。
メモリリークの検出例
つぎは、メモリの解放を忘れた場合の例である。#include <malloc.h>
#include <stdio.h>
int main() {
char* bytes1 = (char*)malloc(5);
char* bytes2 = (char*)malloc(8);
free(bytes2);
return 0;
}
先ほどと同じ 5バイトと 8バイトの割り当てを行った後、8バイトの方のみ解放する。
$ ./Test2
HEAP REPORT
SUMMARY
total allocated: 13(bytes) 2
memory leak: 5(bytes) 1
DETAIL OF MEMORY LEAK
no. memory size(byte) created by
1 0xfae00000 5
SUMMARYには、total allocatedに 2回の割り当てで合計 13バイト割り当て、memory leakに そのうちの 1回分の 5バイトが解放漏れを起こしたことが示されている。
さらに DETAIL OF MEMORY LEAKでは、5バイトの解放漏れ領域のアドレスが 0xfea0000であることを示している。
解放漏れが複数ある場合には、その数分だけ 1行ずつ表示される。
created byの欄が空欄なのは許して欲しい。
libdwarfを使用して割り当てたコードのソースファイルとその行番号を表示するためのものだが、現在機能しない。
未割り当て領域へのアクセス検出例
メモリを割り当てていない領域にアクセスする例を示す。#include <malloc.h>
#include <stdio.h>
int main() {
char* bytes = (char*)malloc(5);
char* p = bytes - 1;
char c = *p;
free(bytes);
return 0;
}
このプログラムは 5バイトの領域を割り当てるが、それよりも前のアドレスから読み出しを行おうとする。
$ ./Test3
not allocated memory access at 0xfadfffff from (0xfeee01bf)
HEAP REPORT
SUMMARY
total allocated: 5(bytes) 1
memory leak: 5(bytes) 1
DETAIL OF MEMORY LEAK
no. memory size(byte) created by
1 0xfae00000 5
Abort (core dumped)
HEAPレポートよりも前にエラーメッセージとアクセスしたアドレス、およびそのとき実行していたコード付近のアドレスが表示される。
最後は Abortして終了する。コアダンプするかどうかは環境設定による。
この例では、割り当てた 5バイトよりも 1バイト小さいアドレスにアクセスしていてアンダーランが検出できたかのように見えるが、そうではない。
直前に別の用途のために割り当てた領域があれば、エラーは検出できない。
注:コアダンプする設定の場合、ダンプに時間が掛かる分 Abortが表示されてシェルに復帰するまで時間を要する。
二重解放の検出例
既に解放済みの領域をもう一度解放しようとしたらどうなるか。#include <malloc.h>
#include <stdio.h>
int main() {
char* bytes = (char*)malloc(5);
free(bytes);
free(bytes);
return 0;
}
このプログラムでは、5バイトの領域を割り当てたあと、2回つづけて解放を行おうとする。
$ ./Test4
bad memory release.(already released.)
HEAP REPORT
SUMMARY
total allocated: 5(bytes) 1
memory leak: 0(bytes) 0
Abort (core dumped)
HEAP REPORTよりも前にエラーメッセージが表示され、Abortする。
制約
下記のような制約がある。- 現バージョンでは、アンダーラン、オーバランの検出はできない。
- MMUのメモリ保護機構を必要とする。
- メモリ割り当て回数の上限は 256 * 1024回。(変更可)
- ヒープのサイズは 64MB。(変更可)
- 同じヒープサイズでも通常より早くメモリが枯渇する。
- main関数が呼ばれる以前に別スレッドが起動された場合、動作は保証されない。
ライセンス (license)
このライブラリは MITライセンスで公開されている。GPLとのマルチライセンスが計画されていたが、手続きの都合により保留され、とりあえず MITライセンス単独でのリリースとなった。
ダウンロード (download)(下载)
ライブラリのソースは下記からダウンロードできる。lwd_malloc_1.0.tgz
Solarisと Linux用の静的ライブラリ(いづれも Intel 32bit版)も含まれている。