調査方法
// 現在のメモリ使用量を知ることができる
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とたいして変わらん。可読性は良くなるけど。今のところこれが最善かなぁ。