* メモリ管理#7
-(by [[K]], 2022.04.21)
** (1)
-[[a22_memman06]]に書いた通り、ACleanというクラスライブラリを作ってみたら、C言語がまるでガーベージコレクションを持つJavaみたいなことができるようになって、私はとてもいい気分になりました。
-それならば、さらに進めて、Javaのガーベージコレクションに匹敵することができないかなと考えました。
-Javaのガーベージコレクションには、メモリコンパクションの機能があります。これはメモリの断片化を自動で解消する機能で、これがあれば「合計の空きメモリは十分あるのにメモリ確保をしようとするとエラーになってしまう」という事態を避けられます。
** (2)
-具体的に見ていきます。仮にmalloc域のサイズが1MBしかなかったとします(組み込み環境などを想定)。
ABegin(c1);
ABegin(c2);
TreeA *a = TreeA_init(c2);
TreeB *b = TreeB_init(c1);
FILE *fp = fopen("data.dat", "r");
while (!feof(fp)) {
Data *data = getDataFromFile(c1, fp);
a.insert(data);
b.insert(data);
}
fclose(fp);
a.analyze.print();
AClean_out(c2); // これで、aに属するメモリはすべて解放される.
int *p = aMalloc(128 * 1024, c1); // この128KBのmallocは成功するか?
...
-やっていることは、aとbの2つの何らかの異なるツリー構造があり、ファイルからデータを読み込んで、双方のツリーに追加していきます。そしてaを使って解析をして結果を表示して、用が済んだのでaだけは解放してしまいます。
-しかしこういうことをすると、メモリの中では、aとbのメモリが交互に入り乱れて確保されてしまうのが普通で、c2のclean-outをした段階で空きメモリはひどく断片化することになります。こうしてトータル容量的には十分なのに、メモリが連続していないからメモリ不足エラーになってしまうのです。
** (3)
-Javaの場合、メモリが断片化するとメモリコンパクションが働いて、オブジェクトが移動して空きメモリがつながって、断片化が解消されます。しかしC言語やC++ではそういうことはありません。・・・それで、Javaがうらやましくなって、自作ライブラリでメモリコンパクションをやろうとしましたが、簡単な仕組みでいい感じに動くものが作れずに、この方針はやめにしました。
-それで別の方法を考えました。・・・オブジェクトの移動は難しいので、最初から混ざらないようにすればいいのです。
-そのためには、aのためのmallocなのか、bのためのmallocなのかを区別できるようにする必要があります。区別できれば、以下の方法で小さなメモリ断片ができないようにできます。
--アロケータは大きなメモリを最初に取ってきて、それをmalloc要求があるたびに切り分けて渡す。
--もし足りなくなったら、また大きめのメモリをとってきて、それを小分けにしていく。
-これはつまり、メモリアロケータがmallocの一つだけではなく、複数あればいいということになります。
-AMemAlc ma;みたいなオブジェクトがあって、ma.alloc()とかma.free()みたいにするのが理想的だと思います。これで様々なメモリ確保の戦略を実装できると思います。
--たとえば先の例のように、あとでまとめて開放することがわかっている場合、ma.free()が呼ばれても無視して(=もはやma.deinit()するまで再利用しない)、ma.alloc()については大きなメモリの上から単純に切り取っていくだけのものを用意すれば、それはすごく単純なのですぐに実装できますし、速度もかなり高速になります(割り当てのために空き領域を捜査しないので、その分が高速になります)。
** (4)
-複数のメモリアロケータが使い分けられるようになると、それだけでこんなにいろいろできるんだなあって思いました。