web-dev-qa-db-ja.com

どのPHPスクリプトがメモリをリークしているのを見つけるには?

私の専用サーバーには32GB RAMがあり、メモリは絶えず増え続けており、現在は毎日再起動する必要があります。

メモリリークの場所を見つけるのに苦労しています。オンラインで見つけることができるのは、「Use xdebug」と言う人だけですが、メモリリークの検出に関するxdebugチュートリアルを見つけることができませんでした。関数呼び出しの前後にmemory_get_usageを印刷しようとしましたが、それは正しい方法ですか?

私は多くのPHPスクリプトを実行しています-訪問者からのもの、cronジョブからのもの-どれがメモリリークしているのかを見つけてできるだけ早く修正する必要がありますが、特定の関数がメモリが漏れているかどうか。

関数呼び出しの前後にmemory_get_usageを出力しようとしましたが、それは上がりますが、関数を複数回呼び出しても、もう上がりません。誰かがこれを説明して、PHP関数にメモリリークがあるかどうかを簡単に簡単に判断する方法を教えてください。

29
Guy

さまざまなことができますが、最初はメモリリークの発生を避けるようにしてください。

PHPはスクリプト言語であり、長時間実行されるスクリプト用に設計されていないため、メモリ管理は市場で最高ではありません。しかし、なぜそうすべきなのでしょうか?要求レベルで呼び出されるため、その実行範囲は非常に小さくなります(2〜3秒以内)。

メモリリークに対して何ができますか?

  1. 5.4より前のバージョンを使用している場合、サークル参照はガベージコレクションされないため、サークル参照を処理する必要があります。

  2. スクリプトを継続的に実行する必要がある場合は、別のアプローチを検討することもできます。 while(true)実装を試してみてください。ただし、スクリプトをsupervisorhttp://supervisord.org )でラップし、終了後に呼び出します。そうすることで、メモリリークが発生しないことを100%保証できます。

  3. xdebugを使用して、スクリプトを1つずつプロファイルし、多くのメモリが消費されている場所を見つけることができます。

  4. クラスがもう必要ない場合は、デストラクタを実装して、参照するすべての設定を解除できます。

    public function __destruct(){
        $this->cleanup();
    }
    
    public function cleanup() {
        //cleanup everything from attributes
        foreach (get_class_vars(__CLASS__) as $clsVar => $_) {
            unset($this->$clsVar);
        }
    
        //cleanup all objects inside data array
        if (is_array($this->_data)) {
            foreach ($this->_data as $value) {
                if (is_object($value) && method_exists($value, 'cleanUp')) {
                    $value->cleanUp();
                }
            }
        }
    }
    
  5. PHPガベージコレクションに関するドキュメント http://us3.php.net/manual/en/features.gc.php

  6. グローバル変数は、ガベージコレクションされることはなく、明示的にunsetする必要があるため、避けてください。 ZFやSymfonyのようなフレームワークを使用している場合は、機能しない可能性があるため、使用できない可能性があります。

最後になりましたが、私はもう一度強調したい、PHPは長時間実行するスクリプトには適していません!継続的に実行する必要があることは、PHPのメモリリークで頭を崩してはなりませんが、JavaまたはC#。

22
MatthiasLaug

このphp-extensionを見てください: https://github.com/arnaud-lb/php-memory-profilerGoogle Performance ToolsKCacheGrind 、または QCacheGrind などのツールを使用して、さまざまな形式で情報をダンプし、簡単に分析できます。

6
Slam

私はかなりうまく機能する方法を見つけました:

  1. インストール " php-memprof "エクステンション。 Ubuntuで実行できます:

    Sudo pecl install memprof

  2. google-perftools 」をインストールします。再びUbuntuの場合:

    Sudo apt-get install google-perftools

  3. スクリプトの最初に次のコードを追加します。

    if (function_exists('memprof_enable')) {
        memprof_enable();
    }
    
  4. そして、この周りの場所は、メモリリークを見つけるために必要なものでした。

    if (function_exists("memprof_dump_pprof"))
    {
        $time = microtime(true);
        $f = fopen("/tmp/profile_$time.heap", "w");
        memprof_dump_pprof($f);
        fclose($f);
        echo "Memory profile dumped. ";
    }
    

    私の場合、100回実行するたびに大きなサイクル内にありました。

  5. 実行google-pprof 2つのメモリダンプを比較します。

    google-pprof --web --base=/tmp/profile_17.heap /tmp/profile_18.heap
    

    これにより、ブラウザで次のようなsvg画像が開きます。

    sample from doc

    内部の数字と名前の説明は gperftools documentation にあります

P.S。phpレベルでリークを修正しても、インタープリターにメモリリークがないことを保証しません。私の場合は、sctiptを長期間再起動するだけです。

5
red_led

私はメモリ使用量の専門家ではありませんが、この方法は問題のあるスクリプトの検出に役立つかもしれません。

情報を取得します。1. Apacheアクセスログファイルを使用します2.独自のメモリ使用量ログファイルを作成します( http://www.webhostingtalk.com/showthread.php?t=617742

メモリ使用量が増加する時間を確認し、Apacheアクセスログと比較します。

少なくとも、使用量がゆっくりと一定に増加するのか、特定の時点で開始するのかについての情報を提供します。

幸運を!

1
Hashbrown