環境:CentOS7 + apache2.4(prefork) + PHP5.4
現象
- PHPの処理が終了してもメモリの使用量が回復しない画面(処理)がある。
- MaxRequestsPerChildに達するとapacheのプロセスが終了してメモリ使用量も回復するが、特定の画面だけでその現象が発生しているため、時にはMaxRequestsPerChildに達する前にサーバーのメモリを使い切ることがある。
- というか、複数プロセスあるんだから、普通はそうだよね。
- ぶっちゃけメモリリークなんだろうけど、発生個所が曖昧なのと、特定環境でのみしか発生しないため、調査が難航。
理想
- PHP処理が終了したら、PHPが使ったメモリを解放する。
現実
-
unset
や循環参照の怪しそうなところを潰してみたけれど、環境によって発生コードが異なっていて、そもそもの根本原因がわからない状態なので、どこまで対応すれば完了と言えるのかの根拠がない(運用に乗せられない)。
迷走
- MaxRequestsPerChildを1にする
⇒メモリを大して使わないPHPや、js、css等へのリクエストにも毎回子プロセスを生成するのはパフォーマンスが心配 - 定期的にapacheの再起動を行う
⇒定期的って具体的にいつよ?(少なくとも1日1回夜間に再起動のペースでは間に合わない)
その他、MaxRequestsPerChild=1用のapacheと通常のapacheを用意するとか考えはしましたが、変更点が多くなり影響範囲が広がりすぎるので断念。
採択
- メモリをたくさん使う(ことのある)画面については特定できているので、メモリをたくさん使った場合はMaxRequestsPerChildの値に関係なく、そこでプロセスを終了として、apacheが確保したメモリを解放させる。
- 分かりやすく言うと、太ったPHPに自殺させます。
強制解放
http://php.net/manual/ja/function.apache-child-terminate.php
apache_child_terminate()
ただし、
もし PHP が Apache 1 モジュールとして実行している場合、TRUE を返します。
これが原因なのか、メモリは回復せず。
真・強制解放
function mightRelease(){
//メモリ解放閾値:この値以上にメモリが使われていた場合はプロセスを落とす
$memory_byte_threshold = 1024 * 1024 * 200;
$memory_byte_usage = memory_get_peak_usage(true);
if($memory_byte_usage > $memory_byte_threshold){
//処理終了時に実行する関数を登録
register_shutdown_function(function(){
posix_kill(getmypid(), 28);
});
}
}
この関数をpostExecute的なところで呼び出す。
200MB以上使った画面だけ、プロセスが終了し、メモリが解放されていることを確認。
終了した直後は子プロセス数は減るが、数秒後にはちゃんと規定数まで増えていることも確認。