PHPのガーベジコレクタについて
PHP 5.2までのガーベジコレクタは参照カウント方式です。
循環参照が発生すると、その参照に含まれるオブジェクトが回収できませんので、デーモンなど長時間実行するようなスクリプトを作る場合は循環参照を起こさないように注意する必要があります。
尚、PHP 5.3からは循環参照コレクタが使え、デフォルトで有効になっています。
実際にPHP 5.1.6と5.3.0を比較し、実際にどのような動きになるか以下のコードで試してみました。
<?php ini_set('memory_limit', -1); // gc_disable(); class A { } $loop_count = 1000000; for($i = 1; $i <= $loop_count; $i++) { $a = new A; $a->self = $a; // 循環参照 if (($i % round($loop_count / 10)) == 0) { printf("%10d: %10d KB\n", $i, memory_get_usage() / 1024); } // unset($a->self); } if ( function_exists('memory_get_peak_usage') ) { // (PHP 5 >= 5.2.0) printf("max %10d KB\n", memory_get_peak_usage() / 1024); }
PHP 5.1.6の場合
PHP 5.1.6だと、以下のような結果になります。
循環参照されている状態だと、どんどん増え続けます。
$ php mem_test.php 100000: 31947 KB 200000: 63872 KB 300000: 101942 KB 400000: 127723 KB 500000: 153504 KB 600000: 203861 KB 700000: 229643 KB 800000: 255424 KB 900000: 281205 KB 1000000: 306986 KB
unsetの所のコメントアウトを外して開放するようにすると以下のような結果になります。
循環参照されていないので、メモリの開放は正しく行われます。
100000: 622 KB 200000: 622 KB 300000: 622 KB 400000: 622 KB 500000: 622 KB 600000: 622 KB 700000: 622 KB 800000: 622 KB 900000: 622 KB 1000000: 622 KB
PHP 5.3.0の場合
PHP 5.3.0の結果。一定以上から変化しません。
循環参照コレクタがうまく働いているようです。
$ php mem_test.php 100000: 1582 KB 200000: 1582 KB 300000: 1582 KB 400000: 1582 KB 500000: 1582 KB 600000: 1582 KB 700000: 1582 KB 800000: 1582 KB 900000: 1582 KB 1000000: 1582 KB max 5139 KB
循環参照コレクタの有効/無効の変更はgc_enable/gc_disable関数で行います。
gc_disableの所のコメントアウトを外すと、PHP 5.3.0でもメモリが増え続けます。
$ php mem_test.php 100000: 43125 KB 200000: 85692 KB 300000: 136451 KB 400000: 170827 KB 500000: 205202 KB 600000: 272345 KB 700000: 306720 KB 800000: 341096 KB 900000: 375471 KB 1000000: 409846 KB max 409863 KB
尚、gc_collect_cycles関数で強制的にガベージを収集することができますが、gc_disableで循環参照コレクタを無効にしている時に確保したメモリは収集されないようですので、わざわざ無効にする必要はないと思います。
また、gc_collect_cycles関数を使うと、わずかながらメモリが増えていく現象が見られましたので、ガベージの収集も基本的にはPHPに任せるようにした方が良いようです。
当然正しく開放されるようにプログラムを組むべきですが、長時間実行されるようなスクリプトを作る場合、石橋を叩いて渡るなら5.3以降を使うようにした方が良さそうです。
個人的な意見としては、開発時はgc_disableで開発し、テスト・運用時にgc_enableで動かすのが良いのではないかと思います。