PHP メモリ管理の履歴

調査方法

// 現在のメモリ使用量を知ることができる
echo memory_get_usage();

// メモリの最大量。これを超えると落ちる。
echo ini_get('memory_limit'); 

循環参照をなくすには

case 1 ○

foreach (range(1,1000) as $i){
    $obj = new StdClass;
}

毎回 new してるためメモリが増えそうだが、リファレンスカウントGCのおかげで使われなくなったオブジェクトは解放される。$obj をvar_dumpしてみると、おもしろいことに、#1, #2, #1, #2 と順繰りになっている。

#1が生成→$obj (#1のリファレンスカウントは1)
#2が生成→$obj (#2のリファレンスカウントが1になり、#1が0。よって#1を破棄)
#1が生成→$obj (#1のリファレンスカウントが1になり、#2が0。よって#2を破棄)
...

case 2 ×

foreach (range(1,1000) as $i){                        
    $obj = new StdClass;         // obj1(1)
    $obj->child = new StdClass;  // obj1(1) obj2(1)            
    $obj->child->child = $obj;   // obj1(2) obj2(1)
    unset($obj);                 // obj1(1) obj2(1)
}

循環参照の例。こうするとがメモリっていく。

case 3 ○

foreach (range(1,1000) as $i){                        
    $obj = new StdClass;         // obj1(1)       
    $obj->child = new StdClass;  // obj1(1) obj2(1)            
    $obj->child->child = $obj;   // obj1(2) obj2(1)
    unset($obj->child);          // obj1(1) obj2(0)
}

obj2をunsetすればOK
obj2が破棄されることでobj1への参照もなくなっている?

unset($obj->child); ではなく、unset($obj->child->child); でもOK。
$obj1のリファレンスが0になるから、$obj2もなくなる?

$objというローカル変数を持っていなければならないというのがやっかいなだな。

case 4 ×

class Hoge{
    public $moge;
    function __destruct(){
        unset($this->moge);
    }
}
foreach (range(1,1000) as $i){
    $obj = new Hoge;
    $obj->moge = new Hoge;
    $obj->moge->moge = $obj;
}

destructorとやらを使えばなとかなるじゃね?と思ってやってみたが、だめだった
__destructが呼ばれるのは参照カウントがなくなってかららしい…。

case 5 ○

class Hoge{
    public $moge;
    function destruct(){
        unset($this->moge);
    }
}
foreach (range(1,1000) as $i){
    $obj = new Hoge;
    $obj->moge = new Hoge;
    $obj->moge->moge = $obj;
    $obj->destruct();
}

手動でやればいいだけどさ。case3とたいして変わら。可読性は良くなるけど。今のところこれが最かなぁ。