PHPの循環参照ではまった

symfony+propelで数千回ループするバッチを走らせたところメモリリークしてどうしても途中で処理が終了してしまう。 調べた所、phpのガベコレのシステムで変数を循環参照させてしまうと、変数の参照カウンタが0にならずにメモリを開放してくれないのが原因だった。

propelで one-to-many のリレーションをしているテーブルを扱う場合、 $one_object->addManyObject($many_object); といったメソッドがあるのだがこのメソッドに循環参照するコードが含まれていてメモリリークしていた。数千回ループ回すバッチとかで使用する時は要注意。

public function addManyObject(ManyObject $l)
    {
        $this->collManyObjects[] = $l;
        $l->setOneObject($this); // ここで循環参照している
    }

検証コード:

for($i=0; $i<100; ++$i) {
    $one_object = new OneObject();
    $one_object->addManyObject(new ManyObject());

    echo memory_get_usage() . "\n";
}

出力:

2147424
2149220
2151240
2153580
2155968
2158356
2160744
2163160
2165576
2167992
...
どんどん増える。

解決策はこの手のメソッドは使わない。PHP5.3から解決策が出てくるらしい。symfony+propelはメモリ使うなぁ。