BoehmGCによる早すぎる解放

展示の為に、まとまった量のArcのコードを書いてglazeで実行したらsegvが頻発した。
調べてみるとまだ使用中のオブジェクトを解放してしまっていたようである。

glazeは環境フレームをstd::vectorにオブジェクトを格納する形で実装していたが、もしかして、
std::vector内は参照不可能と判断されてしまい、collectされてしまうのではないか思い、以下のような実験コードを書いて検証してみた。

#include <stdio.h>
#include <sys/types.h>
#include <vector>
#include <gc.h>
#include <gc_cpp.h>
#include <gc/gc_allocator.h>

class object : public gc_cleanup
{
public:
    object(size_t d) { data = d; };
    ~object() {
        // GCによって回収されるときにプリント
        fprintf(stdout, "destructor called id (%ld)\n", data);
        fflush(stdout);
    };

    size_t data;
};

int main() {
    GC_INIT();

    std::vector<object*> tes = std::vector<object*>();

    object* item;
    size_t i;

    for (i = 0; i < 10000; i++) {
        item = new object(i);
        tes.push_back(item);
    }

    sleep(2);

    return 0;
}
  • コンパイルして実行。
$ g++ gctest.cc -lgc -o gctest
$ ./gctest
...
... 省略
...
destructor called id (8778)
destructor called id (9417)
destructor called id (8910)
destructor called id (8779)
destructor called id (9416)
destructor called id (8909)
destructor called id (8776)
destructor called id (9419)
destructor called id (8908)
destructor called id (8777)
destructor called id (9418)
  • ものの見事に全部GCされてしまっている。

上記現象を調べてみるとBoehmGCはstd::vectorやstd::mapと併用する場合は、
それらの定義時にgc_allocator<>やtraceable_allocator<>などというものを指定する必要があるらしい。

参考:http://osdir.com/ml/programming.garbage-collection.boehmgc/2004-06/msg00014.html

まだあまりうまく理解できていないが、これは参照を知らせるためのものかなにかだろうか??
とりあえず、テストコードを以下のようにしてみた。

#include <stdio.h>
#include <sys/types.h>
#include <vector>
#include <gc.h>
#include <gc_cpp.h>
#include <gc/gc_allocator.h>

class object : public gc_cleanup
{
public:
    object(size_t d) { data = d; };
    ~object() {
        fprintf(stdout, "destructor called id (%ld)\n", data);
        fflush(stdout);
    };

    void print() {
        fprintf(stdout, " %ld", data);
        fflush(stdout);
    }

    size_t data;
};

int main() {
    GC_INIT();

    std::vector<object*, traceable_allocator<object*> > tes =
        std::vector<object*, traceable_allocator<object*> >();

    object* item;
    size_t i;

    for (i = 0; i < 5000; i++) {
        // まず5000個挿入
        item = new object(i);
        tes.push_back(item);
    }

    for (size_t j = 0; j < 2000; j++) {
        // 2000個削除(参照不能にする)
        tes.pop_back();
    }

    for (; i < 10000; i++) {
        // さらに5000個挿入
        item = new object(i);
        tes.push_back(item);
    }

    sleep(2);

    std::vector<object*, traceable_allocator<object*> >::iterator k;
    fprintf(stdout, "(");

    for (k = tes.begin(); k != tes.end(); k++)
    {
        (*k)->print();
    }
    fprintf(stdout, ")\n");

    sleep(2);

    return 0;
}
  • コンパイルして実行。
$ g++ gctest.cc -lgc -o gctest
$ ./gctest
...
... 省略
...
destructor called id (3906)
destructor called id (3907)
destructor called id (3908)
( 0 1 2 3 ...省略... 2997 2998 2999 5000 5001 5002 ...省略... 9997 9998 9999)
  • ちゃんと参照不能としたもの(3000-4999)のみがGCされている。

この結果をもとにglazeの実装も直したらうまく動いた。