sfWebDebugで、より正確なメモリ使用量を見るには
symfonyを使っていてMemory Exhaustedというエラーが表示されたことはありませんか?
PHPは5.2.0でメモリ管理の実装が変更されたため、従来に比べて必要とするメモリ量が増加しています(厳密には「それまで正確ではなかったメモリ確保処理を改良して、より正確なメモリ確保を行なうようにしたためメモリ使用量が増えたように見える」ということだそうです)。このため、PHPスクリプトが取得できるメモリの上限値を設定するmemory_limitのデフォルト値もかつての8MからPHP 5.2.0では16Mに、PHP 5.2.1ではさらに128Mまで引き上げられましたが、PHPをバージョンアップする際にソースアーカイブに含まれるphp.ini-distやphp.ini-recommendedを使用しなかったケースではmemory_limitが少なすぎる状態のまま稼働している場合があります。
そのようなmemory_limitの値が充分でない環境でsymfonyのような大きなライブラリを利用すると、冒頭のMemory Exhaustedが発生します。これを防ぐにはmemory_limitを充分な値に変更すればよいのですが、果たして自分のsymfonyプロジェクトはどのくらいのメモリを必要としているのでしょうか?
symfonyではデバッグモードが有効になっていると、ページの右上にsfWebDebugクラスによるWebデバッグツールバーが表示されます。
このツールバーには、ログメッセージやSQLクエリの発行数、所要時間と並んでメモリ確保量が表示されています。この値はmemory_get_usage()の戻り値となっているため、アクションの実行に必要なメモリ量の目安にできそうです。
ところが、このmemory_get_usage()は「確保されたメモリ量の最大値」ではなく「現在の確保されたメモリ量」を返すため、アクションの実行後にフィルターとして起動されるsfWebDebugクラスでは「アクションクラスのメソッド内で確保されたメモリ」までは取得できません。試しにアクションクラスのメソッドに以下のようなコードを追加してみても、Webデバッグツールバーのメモリ表示に大きな変化はありません。
$a = array(); for ($i = 0; $i < 1000000; ++$i) { $a[] = rand(); // 100万個の乱数を配列にセット }
このままではmemory_limitの値を128Mにしてお茶を濁すしかありませんが、幸いPHP 5.2.1以降にはmemory_get_peak_usage()というありがたい関数が存在します。この関数は「PHPによって割り当てられたメモリの最大値を返す」ため、アクションクラス内で確保されたメモリ量を含む値を取得することができそうです。
実際にWebデバッグツールバーでmemory_get_peak_usage()を使うには、symfonyのdebug/sfWebDebug.class.phpを以下のように変更しておきます。
--- sfWebDebug.class.php.old 2007-04-10 01:12:42.000000000 +0900 +++ sfWebDebug.class.php 2007-04-10 02:04:39.000000000 +0900 @@ -274,10 +274,18 @@ // memory used $memoryInfo = ''; - if (sfConfig::get('sf_debug') && function_exists('memory_get_usage')) + if (sfConfig::get('sf_debug')) { - $total_memory = sprintf('%.1f', (memory_get_usage() / 1024)); - $memoryInfo = '<li>'.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/memory.png').' '.$total_memory.' KB</li>'; + $total_memory = 0; + if (function_exists('memory_get_peak_usage')) { + $total_memory = memory_get_peak_usage(); + } else if (function_exists('memory_get_usage')) { + $total_memory = memory_get_usage(); + } + if ($total_memory) { + $total_memory = sprintf('%.1f', ($total_memory / 1024)); + $memoryInfo = '<li>'.image_tag(sfConfig::get('sf_web_debug_web_dir').'/images/memory.png').' '.$total_memory.' KB</li>'; + } } // total time elapsed
変更したsfWebDebugを利用して先ほどの乱数を生成するアクションを実行してみるとちゃんとメモリ確保量が大幅に増えており、実際に必要となるメモリ量をかなり正確に知ることができます。
というわけでMemory Exhaustedが頻発している方はもちろん、そうでない方もsfWebDebugを弄ってみて自分のsymfonyプロジェクトが必要とするメモリ量を把握しておくとよいのではないでしょうか。
なお、symfonyの開発者ブランチには同様の変更がコミットされているものもあるようです。
