Webサイトをスクラップするためのphpスクリプトがあります(テキストファイルのみ)。数時間実行した後、メモリ制限に達したためにスクリプトが停止することに気付きました。制限を増やすことができることはわかっていますが、スクリプトがロードするファイルは1つのHTMLファイルであるため、各ループの後にスクリプトがメモリを空にできない場合にのみ、制限に達することを説明します。スクリプトのメモリを定期的にflush()することで、スクリプトのメモリ管理を最適化できますか?
2 に答える
一般に、PHP ではメモリを手動で管理する必要はありません。これは、Zend Engine に組み込まれている高レベルのメモリ マネージャーがこれを処理してくれるためです。ただし、コードがメモリ不足になる理由をよりよく理解するために、これがどのように機能するかについて少し知っておくと役に立ちます。
非常に基本的な概要として、PHP は、特定のデータを参照している変数の数の「refcount」に基づいてメモリを解放します。つまり$a = 'hello'; $b = $a;
、文字列を含む 1 つのメモリ'hello'
の refcount は 2 になります。unset()
いずれかの変数を呼び出すか、変数がスコープ外にある場合 (たとえば、それらが定義された関数の最後)、refcount は次のようになります。下降。refcount がゼロになると、データが削除され、メモリが解放されます。この場合の「解放」とは、その PHP スクリプトの他の部分で使用できるように解放されることを意味し、必ずしも他のプロセスが使用するためにオペレーティング システムに解放されるとは限らないことに注意してください。
知っておく価値のあるPHP バージョン間の違いがいくつかあります。
- 上記の参照カウント メカニズムは、循環参照(例:
$obj1->foo = $obj2; $obj2->bar = $obj1;
) がある場合は機能しません。参照カウントが 0 になることはないからです。PHP 5.2 以前では、このような循環参照がメモリ リークを引き起こし、プログラマが手動で処理する必要がありました。PHP 5.3 では、特にこのケースを処理するために「ガベージ コレクター」が追加されました。通常の refcount メカニズムを置き換えるものではありませんが、コードで循環参照が一般的である場合は、一読する価値があります。 - PHP 5.4 には、PHP がメモリを割り当てて使用する方法に対する多数の最適化が含まれています。私の知る限り、これらのどれも効率的なコードの書き方に関する基本的な推奨事項を変更するものではなく、可能であれば PHP のバージョンをアップグレードする正当な理由にすぎません。
それ以外にも、メモリを有効に利用する PHP コードを作成するための一般的なヒントがいくつかあります。
- 使用されていない変数は、不要になったら必ず破棄してください。ほとんどの変数は特定の関数に対してローカルであるため、適切に構造化されたプログラムでは、これは多くの場合問題になりません。関数が終了すると、それらはスコープ外になり、解放されます。ただし、大きな中間変数を作成する場合、または多数の変数を動的に作成する場合は、手動で呼び出す
unset()
ことをお勧めします。また、コードが非常に線形である場合、または多数のグローバル変数と静的変数を使用している場合、コードをよりモジュール化された構造にリファクタリングするだけで、メモリ パフォーマンスだけでなく、読みやすさ、保守性などが向上する可能性があります。 - 参照によって変数を代入または渡す ( ) と、単純な代入 ( ) よりも多くのメモリ
$foo = &$bar
を PHP が使用する可能性があります。これは、PHP が「Copy On Write」メカニズムを使用して同じ内容の変数をメモリの 1 つの場所に格納するためですが、参照割り当てがこのメカニズムと競合するため、PHP は変数を早期にコピーする必要があります。$foo = $bar
- オブジェクトは、スカラー値 (int、boolean、string) や配列よりもメモリを大量に消費します。これは PHP 5.4 で大幅に改善されたものの 1 つですが、それでも検討する価値はあります。
変数が不要になったときに、変数の設定を解除できます(例:unset($var)
または$var = null
)。PHP 5.3以降を使用している場合は、ガベージコレクターを明示的に呼び出すこともできます。gc_collect_cycles()およびgc_enable()を参照してください。
一部の機能は他の機能よりも悪いようです。私は最近、それarray_merge_recursive()
が私のコードのメモリフットプリントに恐ろしいことをしたことを発見しました。
メモリの行き先を分析できるようにしたい場合は、XdebugやXHProf/XHGuiなどのツールを使用できます。例:Xdebugとメモリ使用量のトレースおよびXHProfによるプロファイリング
参照: