2012-04-02 8 views
4

私は大量の人を持ち、SOAPを介して外部リソースから詳細を取得し、データを変更して戻します。細部のサイズのために、私はPHPのメモリを128MBに増やしました。実行の約4時間後(おそらく実行に4日かかります)、メモリが不足していました。相続人は、それが何をするかの基本:ガベージコレクタはPHPでどのように動作するのですか

$people = getPeople(); 
foreach ($people as $person) { 
    $data = get_personal_data(); 
    if ($data == "blah") { 
     importToPerson("blah", $person); 
    } else { 
     importToPerson("else", $person); 
    } 
} 

それはメモリを使い果たし、私はforeachループの前に$のデータを初期化することを決定したクラッシュしてtopによると、プロセスのメモリ使用量が7.8%以上に上昇していない後12時間稼働しています。

私の質問は、再利用してもループ内で初期化された変数にガベージコレクタを実行しないのですか?システムはメモリを取り戻していますが、PHPはそれをまだ使用可能とマークしておらず、最終的に再びクラッシュします(私は256MBに上げましたので、2つのものを変更しています。スクリプトはこれに答えるために戻ってきますが、それを理解するためにさらに12時間待たずに)

私はZendフレームワークを使用していないので、このような他の質問は適切ではないと思います。

編集:私は実際にスクリプトやそれが何をしているかに問題はありません。現時点では、すべてのシステム報告に関する限り、私は何の問題もありません。この質問は、ガベージコレクタと、foreachループ内のリソースをどのように/いつ取り戻すか、そして/またはシステムがPHPプロセスのメモリ使用状況を報告する方法に関するものです。

+2

これは2度投票された理由を聞いてみたいと思います... – Moses

+0

'importToPerson()'ではどうなりますか? – PeeHaa

+1

if($ data == "blah"){'?if($ data =" blah "){ – PeeHaa

答えて

0

memory_get_usage()を使用して何が起こっているのかを確認してください。ループの内側に配置して、メモリ割り当ての動作を確認できました。 システムモニタを見てみたことがありますか、そのプロセス中にどれくらいのメモリが使用されているか見てみましたか?

+0

スクリプトatmを一から修正する必要はありません。 Topはシステムモニタであり、メモリ使用量は7.8%を上回っていません。理論的には、メモリの割り当てやスクリプトのメモリ不足は避けてください。 – Rudiger

+0

そして、繰り返し数に制限を設けてこれらの変更を加えて別のスクリプトを実行することはできませんでしたか?つまり、あなたが何をしているのかは完全にはわかりません。 – Norm

+0

このスクリプトは、多くのSOAPリクエスト/データベース操作を行うので、実際にはかなりプロセス集中的です。私が答えを得ることができない場合は、少し後に調査しますが、おそらくスクリプトを変更して1時間ほど実行し、メモリ割り当てを監視し、すすぎ、繰り返します。いくつかの洞察力を与えるために、PHPのガベージコレクタで自分よりも知識が豊富な人が望んでいた。 – Rudiger

2

私はPHPのVMの内部を知りませんが、自分の経験から、ページが実行されている間はガベージコレクトはしません。これは、ページが終了したときにページが作成したものをすべて破棄するためです。

ほとんどの場合、ページのメモリが不足して限界値がかなり高く(128Mbが高くない)、アルゴリズムの問​​題があります。多くのPHPプログラマは、データの構造を組み立て、それを次のステップに渡して構造を繰り返します。通常は別のものを作成します。泡立ち、すすぎ、繰り返します。残念ながら、このアプローチは大量のメモリを必要とし、メモリに複数のデータコピーを作成することになります。 PHP 5では、オブジェクトが参照カウントされ、コピーされず、文字列サブシステム全体がはるかに高速化されたという2つの大きな変化がありました。しかし、それはまだ問題です。

メモリの使用を最小限に抑えるには、アルゴリズムを再構成して最初から最後までのデータを処理できるようにします。それから、あなたは次のものを手に入れ、再び始める。最良の場合のシナリオは、データセット全体をメモリに保持することはないということです。データベースでサポートされているWebサイトの場合、これはデータベースクエリのデータ行をプレゼンテーションまですべて処理してから次のページを取得することを意味します。もちろん、このアプローチは必ずしも可能ではなく、スクリプトはメモリ内の膨大なデータを保持しなければなりません。

つまり、データの一部に対してこのようなメモリ節約のアプローチを行うことができます。トリックは明示的にunset()キー変数またはループの最後に2つのキー変数です。このスペースを再生する必要があります。もう1つの「ベストプラクティス」トリックは、ループ内にある必要のないループデータ操作からシフトすることです。あなたが発見したようです。

1Gb以上のメモリが必要なPHPスクリプトを実行しました。実際にスクリプトごとにメモリ制限を設定することができますini_set('memory_limit', '1G');

+2

PHP 5.3で「本当の」ガベージコレクタが追加されました。それはまだ不完全ですが、それはあなたが記述しているものよりも改善です。 – duskwuff

+0

実際にはコマンドラインで実行されています。私は毎回(すべての行ではなく、返された配列を反復する)行を行うことを考えましたが、余分なデータベースクエリがその利点を無効にすると感じました。 – Rudiger

+1

各行を処理するために巨大なループがあるのは当然ですが、以前の結果をフェッチしているときに新しいクエリを実行しようとすると、リソースの問題が発生しやすくなります。また、重いSQLクエリよりも多くの小さなSQLクエリを実行する方が速いこともあります。 – staticsan