PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 32 bytes)
このエラーが出た時は、php.iniのmemory_limitを変更すれば、防げるようになるのですが、php.iniを変更できなかったり、変更したくないことも多いと思います。。
今回は、php.iniを変更したくなかったので、設定以外で対応したいと思っていたところ、Doctrineにはfree()というものがあり、オブジェクトを解放できることがわかりました。
free()は、Doctrine_RecordとDoctrine_Query、Doctrine_Collectionで使うことができるみたいです。
試しにテーブルから5000件抽出し、ループでupdated_atカラムを更新するプログラムを実行してみました。
// Usersテーブルから5000件のデータを抽出
$q = Doctrine_Query::create()
->from('User u')
->limit(5000)
->orderBy('u.id ASC')
->execute();
foreach ($q as $k => $val) {
$val->updated_at = date("Y-m-d H:i:s");
$val->save();
if ($k % 500 == 0){
printf("%d : %dKB\n", $k, (memory_get_usage(true) / 1024));
}
$val->free();
}
$q->free();
printf("End : %dKB\n", $k, (memory_get_usage(true) / 1024));
効果があるのかを判定するために、まずは$val->free()をコメントアウトして実行してみました。
0 : 94976KB
500 : 99072KB
1000 : 102912KB
1500 : 106752KB
2000 : 111616KB
2500 : 115712KB
3000 : 119552KB
3500 : 123392KB
4000 : 127232KB
4500 : 131840KB
End : 137728KB
実行結果からもわかるように、メモリがどんどん増えていきます。今回はこれを増えないようにしたいので、$val->free()を有効にして実行してみます。
0 : 94976KB
500 : 95488KB
1000 : 95744KB
1500 : 96000KB
2000 : 97024KB
2500 : 97024KB
3000 : 97024KB
3500 : 97024KB
4000 : 97024KB
4500 : 97536KB
End : 99584KB
多少は増えますが、なにもしないときよりも38144KBのメモリを抑えることができましたので、大量にデータを扱うときは対策をするべきだなと思いました。
また、テーブルを結合している場合は、引数にtrueを渡してfree(true)と指定することで、リレーションのオブジェクトもすべて解放することができます。