久しぶりにYii2の話題です。ちょっと嵌まりかけたのでメモしておきます。
エラーの内容
Yii2で作ったシステムを別のサーバに持って行く必要があり、そこで動かしてみたところ、以下のエラーが表示されました。
Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes)
phpのメモリ使用量はデフォルトでは128MBに設定されているようで、それ以上使おうとするとこのエラーになります。
元の環境だとデフォルトが変更されていたので気づきいていませんでした。
処理の内容
処理の内容は、CSVからデータを読み取って、データベースに入れていくというもので、擬似的なコードだと以下のようなもの。
while (($array = fgetcsv($fp) !== false) { $model = new Model(); $model->attr = $array[0]; ... $model->save(); }
この処理を続けていると、いつまでもメモリが増え続けるので、メモリリークだと思ったわけです。
メモリの使用量の調査方法
memory_get_usage() で現在のメモリ使用量が返されるので、以下のようにするとメモリの使用量がログに出されます。
Yii::warning("MEMORY: " . memory_get_usage());
ちなみに、phpにガーベージコレクションを動作させるgc_collect_cycles()という関数がありますが、今回の場合、これは全然効果無かったです。
わかったこと
調査の過程は省きますが、処理のところの$model->save()をコメントアウトするとメモリ使用量が増えなくなります。
ググってみたところ、以下のサイトを見つけました。
forum.yiiframework.com
つまり、デバッグモードだと起きる問題らしい。デバッグモードで動作させるとデータベースへのクエリーを表示させることができるので、そういう情報をメモリに保存しているということでしょうね。
解決方法
デバッグモードで使用する場合には、phpで使えるメモリを増やす。本番運用に入るときには、デバッグモードを使わないということになります。
phpで使えるメモリを増やすには、いくつか方法があるようですが、.httaccessに以下を追加するのが簡単そうです。
php_value memory_limit 256M
デバッグモードを使わなくする方法
(backend/frontend)/config/main-local.phpの以下を削除するとデバッグモードが使われなくなります。
if (!YII_ENV_TEST) { // configuration adjustments for 'dev' environment $config['bootstrap'][] = 'debug'; $config['modules']['debug'] = [ 'class' => 'yii\debug\Module', ]; $config['bootstrap'][] = 'gii'; $config['modules']['gii'] = [ 'class' => 'yii\gii\Module', ]; }
というかちょっとこれおかしい気がする。コメントにdev環境での調整と書いてあるのに、testでない場合になっている。多分、以下のようにdevの場合にはとするべきだと思う。
-if (!YII_ENV_TEST) { +if (YII_ENV_DEV) { // configuration adjustments for 'dev' environment
そうすれば、(backend/frontend)/web/index.php を切り替えることでデバッグモードの有効/無効が切り替えれます。
開発環境: defined('YII_ENV') or define('YII_ENV', 'dev'); 本番環境: defined('YII_ENV') or define('YII_ENV', 'prod');
デバッグモードを使わなくすると、ある程度メモリ使用量が増えた後は、ずっと繰り返していてもメモリの使用量は一定量で収まるようになりました。