gc.h ファイルをインクルードします。
その後、
プログラム中の以下のメモリ関数を
Boehm GC ライブラリのものに
置き換えて下さい。
元の関数 |
置き換える関数 |
malloc |
GC_malloc |
calloc |
GC_calloc |
realloc |
GC_realloc |
free |
消去 |
修正を加えたプログラムが
tree2.c です。
-lgcオプションを加えてコンパイルしましょう。
gcc -o tree2 tree2.c -lgc
元のプログラムに対して、
修正を行うのが嫌なら
以下のようなヘッダーファイルを用意するのが
よいでしょう。
#include <gc.h>
#define malloc GC_malloc
#define calloc GC_calloc
#define realloc GC_realloc
#define free
これで元のプログラムは Boehm GC を
使うように変更できました。
これでおしまいです。
その他の基本機能
Boehm GC の基本機能として、
malloc 系の関数の置き換え関数以外に、
以下のような関数が存在する。
- void GC_gcollect(void)
- 明示的に GC を発生させる関数。
プログラムのフェーズの変わり目などに
この関数を呼ぶことで、
Boehm GC が管理している領域のクリーンナップが
可能になる。
- size_t GC_size(GC_PTR object_addr)
- GC_malloc などの関数で確保したメモリ領域の
サイズを返す関数。
Boehm GC はメモリ割り付け時に、
領域のサイズの大きさをメモリ領域にかきこんでおくので、
それを読み取ってくる関数である。
当然、
obj_addr は
Boehm GC によって割り付けられた
メモリ領域の先頭アドレスである必要がある。
char* p = (char*)GC_malloc(1000);
size = GC_size(p); /* GC_size は 1000 を返す。*/
- GC_PTR GC_base(GC_PTR displaced_pointer)
- この関数は、
構造体や配列のような大きなサイズのオブジェクトがあり、
displaced_pointerが
その内部のアドレスを指している時、
オブジェクトの先頭アドレスを返してくれる関数である。
char *p, *q, *r
p = (char*)GC_malloc(1000);
q = p + index ;
r = GC_base(q); /* r は p と等しくなる。*/
displaced_pointerは
Boehm GC によって割り付けられ、
まだ回収されていないオブジェクトの内部を指す必要がある。
この関数は、
常に先頭アドレスを見つけられるとは限らない。
分からない場合には、0 を返してくる。
また、Boehm GC によって管理されていない領域のポインタを、
displaced_pointerにセットして渡し場合、
エラーが発生する危険性がある。
- GC_PTR GC_malloc_uncollectable(size_t size_in_bytes)
- この関数は Boehm GC の管理している領域に、
GC によって回収されないメモリ領域を陽にとることが
できる。
この関数で割り当てたオブジェクトは GC_free で回収する
必要がある。
- void GC_free(GC_PTR object_addr)
- Boehm GC が割り当てたオブジェクトを
明示的に解放する関数。
主として GC_malloc_uncollectable で割り当てた
オブジェクトを回収する。
object_addrで指定できるのは、
Boehm GC によって割り付けられたオブジェクトのみで、
でたらめなアドレスや malloc/calloc によって取ってきた
オブジェクトを指定されると、
エラーとなる可能性がある。
GC_free(0)の場合、
何も操作をしない(安全性は保証されている)。
GC の動作状況を知る
サンプルプログラムは何の出力も行いませんでしたから、
本当に GC が行われたのかどうか釈然としません。
GC が行われたことをきちんと確かめましょう。
gc.hに定義されているGC_gc_noは、
Boehm GC ライブラリが起動してから何回 GC を
起こしたのかをカウントする変数です。
GC_gc_no は gc.h の中で
以下のように定義されています。
GC_word は unsigned longの
別名となっています。
//typedef unsigned long GC_word;
GC_word GC_gc_no;
サンプルプログラムのメインループの繰り返しに、
GC_gc_no を表示するコードを
挿入してみました。
for(i = 0; i<100 ; i++){
Tree* root = generate_tree(100);
printf("GC counts: %d\n", GC_gc_no );
}
GC 回数以外に、
Boehm GC が管理しているメモリのパラメータを知ることが可能です。
関数 |
解説 |
size_t GC_get_heap_size(void) |
(Boehm GC が管理する)ヒープ領域の大きさサイズ |
size_t GC_get_free_bytes(void) |
(同)ヒープ領域中の空き領域 |
size_t GC_get_bytes_since_gc(void) |
最後の GC が起きてから割り付けたオブジェクトのサイズ |
size_t GC_get_total_bytes(void) |
プロセスが起動してから割り付けたオブジェクトの総サイズ |
tree3.cは、
サンプルプログラムを GC の状況を報告するように
修正したものです。
この方法は、プログラム内部から Boehm GC の状況を
知ることができるのですが、
Java の -verbose:gc 表示のように、
GC が起こる度に状況を報告して欲しい場合があります。
Boehm GC では、
ライブラリコンパイル時にこの機能がオフになっています。
Makefile の中のコンパイラにあたえるマクロ定義 DEFS
の中にある -DSILENT を削除してビルドすることによって、
この機能に対応します。
# DEFS = ... -DSILENT=1 ...
DEFS = ... ...
SILENTマクロを外してビルドしたライブラリを使うと、
GC が起こる度に大量の内部状況を出力するようになります。
--> Marking for collection 11 after 9007104 allocd bytes + 49152 wasted bytes
Collection 10 reclaimed 8192 bytes ---> heapsize = 20586496 bytes
World-stopped marking took 210 msecs
Bytes recovered before sweep - f.l. count = 0
Immediately reclaimed 8192 bytes in heap of size 20586496 bytes
16384 (atomic) + 20561920 (composite) collectable bytes in use
Finalize + initiate sweep took 0 + 0 msecs
Complete collection took 220 msecs
Increasing heap size by 6864896 after 4096 allocated bytes
Increasing heap size by 8388608 after 6864896 allocated bytes
Initiating full world-stop collection 12 after 15253504 allocd bytes
4096 bytes in heap blacklisted for interior pointers
この出力情報は詳細で、GC に掛かった時間やメモリサイズ等の情報を得ること
ができます。
ただし、この出力がいつも出ていると鬱陶しいので
プログラムの適当な箇所で GC_quietに 0 以外の値を代入すること停止させましょう。
停止した GC 出力は、GC_quiet を 0 にすることで再開することもできます。
戻る
Written by NAKAMURA Minoru, Email: nminoru atmark nminoru dot jp, Twitter:@nminoru_jp