C言語でGCを利用する
ツイートGC
今回は外部ライブラリの説明となります。番外編といったところでしょうか。
C/C++において、メモリ管理は最大の問題と言ってもよいでしょう。C++になって多少はマシにはなりましたが何も解決していません。
少し前のWindowsのブルーバックを見れば分かるでしょう。本質的にはC/C++の問題というよりは、PCのハードウェアの仕様上仕方ないのですが。
Boehm GCはC/C++に対応したガーベージコレクタで、保守的GCと呼ばれる戦略を採用しています。
ここを見ると分かるように、w3m、Mozilla、Monoなど有名なプロジェクトで採用されています。
使い方は簡単ですのでこれを機会に是非使って見ませんか?
Boehm GCのインストール
まずはインストールから。最近のLinuxであればたいてい通常の方法でインストールできます。
例えばこのようにインストールができます。
# yum install gc-devel #RedHat系 # apt-get install libgc-dev #Debian系
もちろんソースからインストールしても問題ありません。
Boehm GC
問題のあるプログラム
このような単純なプログラムを例にします。
これは見たままで、free(3)されてないのでメモリーリークしています。
もちろんこれは非常に単純な例ですが、数千行もある複雑なプログラムでこういったコードは珍しくありません。
#include <stdio.h> #include <stdlib.h> void memory_test(void) { char *p; p = malloc(1024); } int main(void) { memory_test(); return 0; }
では実際にコンパイルしてみましょう。
$ gcc -o memory_test memory_test.c $ ./memory_test $ echo $? 0
実行するとエラーも出ず問題なく実行できます。
これは当然で、プログラム終了時にはOSがすべての資源(メモリやファイルディスクリプタ等)はすべて回収してくれるからです。
しかしこれは問題です。例えば、memory_test関数をforでループさせればその分メモリがfree(3)されずに行方不明になるのです。
Boehm GCの基本的な使い方
ではこのプログラムでBoehm GCを使います。
Boehm GCはmalloc(3)、realloc(3)の代わりにGC_malloc(),GC_malloc_atomic(),GC_realloc()を使います。
free(3)にはGC_free()という関数がありますが、基本free(3)は使わなくて問題ありません。
GC_malloc(),GC_malloc_atomic()の違いは、GC_malloc_atomic()が内部的にポインタを含まない場合に使います。関数内部でメモリ領域のポインタチェックを行うか行わないかの違いで、せいぜい動作速度に多少の差が出る程度です。そこまでシビアで無いならばGC_malloc()で良いでしょう。
つまり、malloc(3)、reallc(3)、free(3)をそれぞれ置き換える作業だけです。
#include <stdio.h> #include <stdlib.h> #include <gc.h> //Boehm GCに置き換える #define malloc GC_malloc #define realloc GC_realloc #define calloc(m,n) GC_malloc((m)*(n)) //使う場合はこのようにする #define free //削除 void memory_test(void) { char *p; p = (char *)malloc(1024); //ワーニング対策でキャスト } int main(void) { memory_test(); return 0; }
基本的にはdefineで置き換えます。GCなのでfree(3)は必要ありませんので削除します。
注意事項として"gc.h"のインクルードと、コンパイル時に"-lgc"が必要な事の2点です。
$ gcc -o memory_test memory_test.c -lgc
非常に簡単ですね。これだけでGCが使えるようになり、メモリリークの恐怖から開放されます。
これは是非使うべきでしょう。
Boehm GCでデバッグ
GC_malloc等の関数を紹介しましたが、さらにGC_debug_mallocと言ったdebug付きの関数も提供されています。
これを使えばデバッグ情報が出力されます。例えば次のようなプログラムがあります。
#include <stdio.h> #include <stdlib.h> #include <gc.h> //Boehm GCに置き換える //debug用 #define malloc(size) GC_debug_malloc(size, __FILE__, __LINE__) #define realloc(p, size) GC_debug_realloc(p, size, __FILE__, __LINE__) #define free GC_debug_free void memory_test(void) { char *p; p = (char *)malloc(1024); p++; free(p); //free出来ない!! } int main(void) { memory_test(); return 0; }
通常ならば、free時にSIGSEGV等が発生しますが、Boehm GCはこれをレポートしてくれます。
ではコンパイルして実行してみましょう。コンパイル時には"-DFIND_LEAK"が必要です。
$ gcc -DFIND_LEAK -o memory_test memory_test.c -lgc $ ./memory_test GC_debug_free called on pointer 0x9b95ab1 wo debugging info
見事にチェックがされています。
次はメモリリークを引き起こすパターンです。
#include <stdio.h> #include <stdlib.h> #include <gc.h> //Boehm GCに置き換える //debug用 #define malloc(size) GC_debug_malloc(size, __FILE__, __LINE__) #define realloc(p, size) GC_debug_realloc(p, size, __FILE__, __LINE__) #define free GC_debug_free void memory_test(void) { char *p; GC_find_leak = 1; //これが必要 p = (char *)malloc(1024); p = (char *)malloc(1024); GC_gcollect(); //メモリリークのチェックを行う } int main(void) { memory_test(); return 0; }
注意事項は、"GC_find_leak = 1"が必要なことと、"GC_gcollect()"でメモリリークチェックを行うタイミングを明示しておきます。
ではコンパイルして実行してみましょう。この場合もコンパイル時に"-DFIND_LEAK"が必要です。
$ gcc -DFIND_LEAK -o memory_test memory_test.c -lgc $ ./memory_test GC_debug_free called on pointer 0x9b95ab1 wo debugging info Leaked composite object at 0x9076ab0 (memory_test.c:21, sz=1024, NORMAL)
素晴らしい! 見事にチェックがされています。
たとえGCが動いていても、こういった動作はプログラマの意図しないケースですので何らかの悪影響が出ると考えてよいでしょう。
リリース前には必ずこういった形でテストをしたいものです。
Boehm GCの他の関数
このようにBoehm GCは様々な機能があります。当然他にも便利な関数があります。
READMEからいくつか紹介します。
関数名 | 説明 |
---|---|
GC_expand_hp(bytes) | 明示的にヒープサイズを増やします |
GC_enable_incremental() | Generational GC、Incremental GCを有効にする。 アルゴリズムの説明はここでは省略します。 |
GC_gcollect() | 明示的にGCを発生させます。 |
まとめ
Boehm GCは簡単に導入が出来、多くの有名なOSSで採用されています。
もちろん完璧ではありませんし、GCに頼るとメモリ効率が悪くなるなどの問題点もありますが、組み込み用途などの特殊な場合以外では十分有効です。
メモリ管理がきちんと出来ることは大事ですが、楽を覚える事も大事です。これを機会に是非利用してみるのも良いでしょう。
日本語で書かれた世界でも珍しいガーベージコレクション専門の本です。GCの実装に興味のある方は是非参考にしてください
秀和システム
売り上げランキング: 237644
洋書ではこれが有名でしょう。英語が読めるのであれば是非!
Chapman and Hall/CRC
売り上げランキング: 64411