LoginSignup
51
46

More than 5 years have passed since last update.

メモリを使いすぎたPHPから無理やり解放させる

Posted at

環境: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以上使った画面だけ、プロセスが終了し、メモリが解放されていることを確認。
終了した直後は子プロセス数は減るが、数秒後にはちゃんと規定数まで増えていることも確認。

51
46
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
51
46