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 と順繰りになっている。
が生成→$obj (#1のリファレンスカウントは1)
unknown plugin(2)が生成→$obj (#2のリファレンスカウントが1になり、#1が0。よって#1を破棄)
unknown plugin(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; unset($obj->moge); }
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(); }