- ベストアンサー
インスタンス化に伴うメモリ消費への対策(メモリの解放)について
いつもお世話になっております。 環境:PHP5,SQLite3 PHPスクリプト(1ファイル)内において、 複数のインスタンスを扱うことがあります。 つまり、スクリプトコード内に「new演算子」が複数登場するわけですが、 この際、サーバーのメモリ内に、何らかのデータが一時的に保存されるようですね? で、インスタンスを増やせば増やすほど、 そのメモリ消費量も増え、 スクリプトの処理速度に悪影響を及ぼすようなので、 これを回避するためのテクニックを教えて頂きたいなと思っております。 「インスタンスして、それが用済みになったら、削除し、メモリを解放せよ」 というようなことを、どこかで読んだことがあるのですが、 具体的には、どのような方法になりますでしょうか。 //インスタンス化 $obj = new MyClass; /* ~処理~ */ //MyClass用済み決定 unset($obj); こんな感じでしょうか? 実際的な方法について、どなたかアドバイスして下さい。 よろしくお願いします。
- みんなの回答 (7)
- 専門家の回答
質問者が選んだベストアンサー
PHPスクリプトは、処理が全て終了すると共に、メモリは開放されます。 よほど、長い処理をする物(XMLのパーサのようなものや解析プログラムなどループ処理でゴリゴリ処理するようなもの)でないのであれば、すぐに開放されますし、 そんなに気にしなくても良いのではないかと思います(gwagawさんが仰ってる通りですね) ただ、数千行ある配列(file関数などで巨大なCSVファイルを引っ張ってきたりとか)や、大量なメンバ変数を有するクラスなどを、一度の処理中に何個も生成したりする場合は、利用が終わったらunsetしたほうが良いですね。 (php.iniで設定している最大メモリ使用量をオーバーすることも考えられますし) 後は、APCなどのキャッシュ機構や、Apacheの同時アクセス数の制御を行う機構や、サーバの物理メモリなどに任せる感じでも良い気がします。 ※あまりメモリの使用量に関して、考えたことが無いので、問題のあることを言っているかも知れません・・・
その他の回答 (6)
- hogehoge78
- ベストアンサー率80% (433/539)
今回例に挙げたどちらのスクリプトも、ループも配列への格納処理も行ってると思いますよ。 ただ、parse_html関数のほうは、一度取得した部分を全体の文字列から削除して、ループする毎に、データが小さくなるように工夫しているようですね。 確かに、そうすれば毎回同じ正規表現を叩いても、次回から値が小さくなるのでだんだんと処理が早くなり、トータルで速度が上がるのでしょうね。
お礼
お礼から書きます。 (が、補足は使わない可能性もあります。) 今回もありがとうございます。 前回、偉そうな分析を書いた私ですが、 >今回例に挙げたどちらのスクリプトも、ループも配列への格納処理も行ってると思いますよ。 と言われ、とても恥ずかしくなりました。 確かに!whileもarrayも使われていましたね。笑 >ループする毎に、データが小さくなるように工夫 私のスクリプトでも同様に、 file_get_contents()で取得したHTMLソースの文字列に対し、 そうした工夫をしたら、速くなるかもしれませんね。 で、さらに、while、arrayを使わないソースを書く、と。 うわー、ちょっと楽しみだ。 やってみます。笑 //------------------- ある一連の処理をPHPで書く場合、 その方法は、多くの場合、何通りにも書けるかと思います。 しかし、出力される結果は同じでも、その反応速度は、 意外とまちまちです。 今回の、私のスクリプトにおいて、whileやarrayを使うのをやめたら、 約2倍速くなった例のように。 そうした、速さを改善するのに役立つ 関数処理速度表 もしくは、 高速化意識系アルゴリズムなるものがあれば、 便利なのになー、なんて思うのですが、 そんな便利なものはないですよね?笑 arrayを使うことは、ハードに対し、 どのような処理をさせているのか等、 ハードとソフトの関係について理解していると、 この辺りのことは感覚的に分かりやすいのかもしれませんね。
補足
>データが小さくなるように工夫 これ、やってみました。 といっても、私なりにやってみた、という感じです。 で、何をしたかと言いますと、 ながーいhtmlソースのうちの、 ある限られた部分に抽出したい情報が集中しているので、 そこらへんだけをまずカットしちゃって、 そのカットしたエリア限定で、繰り返し、 正規表現で該当箇所を抽出する、なんて処理に変えてみました。 結果はと言うと、 加工しない、長いhtmlソースをそのまま使った方法に比べ、 おおよその平均値で10%~20%くらい速くなりました。 ちなみに、 この処理速度の計測には、例の技術が使われております。笑 カットの方法も色々ありそうですが、 該当エリアの開始サイン、終了サインを見つけ、 そのサインが、何文字目に登場するかを探り、 それをもとに開始~終了までの文字列をカットしてくる方法と、 もう大雑把に、 大体、該当エリアってのは、 20000文字目~40000文字目くらいだろうから、 その辺でカットしてきちゃえ、てやんでぇ! ってな感じで、該当エリアをざっくり抜き出すなんて方法とを 考えました。 よって、3通り試しました。 (1)長いままの生ソースから抽出する方法、 (2)必要最小限のサイズでカットしてきてから抽出する方法、 (3)大雑把にカットしてきてから抽出する方法、 の3通り。 結果は、(3)が速かったように思います。笑 (2)と(3)は似ていますが、 (2)は、下処理にやや時間が掛かっている気がします。 もっとも、HTMLソースが短い場合には、言うまでもなく、 これらの速度差はあまり感じられないですね。 (ちなみに、スピードチェックでは、差を分かりやすくするために、ソースの長さをかなり長くして試しました。)
- hogehoge78
- ベストアンサー率80% (433/539)
結局のところ、70kbあるファイルに対して何をしているのかが明確に分からないので、近いライブラリというのがどういうものかも不明瞭ですが、 HTMLをパースして、オブジェクトや配列に直すというのが近いと仮定すると、 http://simplehtmldom.sourceforge.net/ こちらのPHP Simple HTML DOM Parserなんかはどうでしょう。 HTMLファイルを文字列でパースしてオブジェクトにします。 http://digit.que.ne.jp/work/wiki.cgi?PHP%E3%83%A1%E3%83%A2%2FHTML%E3%81%AE%E8%A7%A3%E6%9E%90 こちらの関数は、正規表現でHTMLを解析するというものです。
補足
>こちらの関数は、正規表現でHTMLを解析するというものです。 あ~、私のしようとしていることに、なんとなく似ていました。 ということで、早速、こちらがどんな動きをするのか試用してみましたところ、 ブラウザに出力される情報量が多い!なのに、速い! 同じhtmlソースを使っているというのに、私のスクリプトより高性能なわけです。 そこで、自分のスクリプトの何が悪いのか、アレコレいじって、私なりに調べてみました。 その結果、処理速度を上げる方法が1つ見つかりました。 ただ単に、 それまでの方法に比べ、相対的に処理が速くなる方法を1つ見つけたに過ぎないのですが。 で、その方法ですと、速度が体感的に(自分で数えたりなんかして)、倍、速いのです。 4秒の処理が2秒に。これは大幅なスピードアップです。 その方法とは! array、whileを使わない、という単純なものです。笑 前回の補足で、配列に正規表現パターンを入れ、 そのパターンの数だけ、whileでpreg_matchする、なんてお話をしましたが、 その方法を、原始的に1つずつ手書きして処理する方法に換えた所、 速くなったというわけです。 ただ、その副産物として、ソースはかなり冗長になり、 また、見た目の美しさも悪くなります。笑 私はまだ駆け出しの日曜プログラマなので、 何を重視してソースを書くべきかがよく分かっていないようです。 結果的に、スクリプトが速く正確に動く、 これをまず第一に考え、 つづいて、保守性の高さ、 なんかを考えますが、 これらを達成する上で、 同じ事の繰り返し処理を書く必要が出てきた場合に、 ちょっと前までの私であれば、迷わず、 配列やwhileなどを多用し、ソースをスッキリさせることを考えていました。 (馬鹿の1つ覚えのように、覚え立てのスマートな道具達を片っ端から使っている、という状態) しかーし! 今回わかったことですが、 ソースの整理整頓が保守性向上に好影響をもたらすことには、疑いの余地はありませんが、 処理速度を速くするかどうかについては、ケースバイケースなのだなと、 そのことに気付いたわけです。 配列やwhileは、確かに便利です。 どちらも、ソースの省スペース化に役立ち、 それは同時に、手書きする人間の手間暇を減らすことにもつながります。 (また、仕組み的に、動的に対応せざるを得ない場合には、配列やwhileを使わないと スクリプト作成不可能な場合もあるかもしれませんね。) その意味では、arrayもwhileも便利な道具なのでしょうが、 単純に、変数が多くなったから配列化しとこー、とか、 繰り返し処理がちょっと多くなってきたから、この際、whileで書いておこー、 なんて気持ちでは使うべきではないなと感じたわけです。 必要であれば使うべきだけれども、使わないですみそうなら、 使わないにこしたことはない、みたいな印象を受けました。 (かなり、過激なことを言っているかもしれません。笑)←ツッコミ大歓迎です 例えば、 変数が10個、繰り返し処理が10回、なんて場合は、 arrayやwhileは試用せずに、手書きでゴリゴリ10回書いた方が速い、 のではと、今回思ったわけです。 今回のこの1例だけで結論を下すのは早計かもしれませんが、そんな風に思いました。 こういった、PHPのコマゴマとした論議も、いつかまとめて、 させて頂きたいと思っていたりしますが、ここではこのくらいにしておきます。笑 (余談ですが、毎回、gooの字数制限と格闘しています。苦笑)
- hogehoge78
- ベストアンサー率80% (433/539)
下記の処理内容に関しまして、なんともいえません。 大きなファイルに対して、正規表現で何度もチェックを行ったり、 その取得した値をさらに何度も加工したりといったことが繰り返し行われているのであれば、遅くなるとは思います。 また、繰り返し処理、加工処理が多ければ、CPU負荷もかかりますので、CPUの処理能力の問題な気もします。 一度、オープンソースのライブラリや、誰かの作成されたサンプルと見比べて、どの程度処理が違うかとか確認してみるのもいいかもしれません。
お礼
>下記の処理内容に関しまして、なんともいえません。 そうでしたか。。。笑 そんなに簡単に分かるような話でもないのでしょうね。 情報の少ない中、私のために考えて下さり、どうもありがとうございました。 >CPUの処理能力の問題な気も なるほど! ということで、早速ですが、ハードウェアのカテゴリで、 マシンに関する質問をしてきました。笑 ちなみに、遅い!と感じているサーバのマシンスペックは、 FreeBSD 6.2 Pentium(R) M processor 1.73GHz メモリ1GB こんな感じです(レンタルサーバ/共有 です。) (ド素人の私から見ても、なんとなくショボそうな気がしています。) >オープンソースのライブラリや、誰かの作成されたサンプルと見比べて、どの程度処理が違うかとか確認してみるのもいいかも オープンソースのライブラリで、何か、お勧めのものはありますでしょうか。 もしよろしければ教えて下さい。よろしくお願い致します。
- hogehoge78
- ベストアンサー率80% (433/539)
http://jp2.php.net/manual/ja/function.memory-get-usage.php memory_get_usage関数を使うと、現在割り当てられているメモリ量を取得できます。 memory_get_peak_usage関数を使うと、スクリプトに割り当てられたメモリの最大値が取得できます。 スクリプトの処理が完了するのに、10秒かかるとのことですが、どのようなスクリプトでしょうか。 ファイルの書き込み処理に時間がかかっているとか、そういうものではないですか? 書き込み処理の場合ファイルシステムなどにも影響されますし、 SQLiteへ大量のinsert文を発行したとかといった場合も、遅くなります。(トランザクション処理を行うと軽減されますが。)
補足
いつもありがとうございます。 今回も宜しくお願い致します。 メモリ関連の関数について教えて頂き、ありがとうございます。 まだ利用していないので、現時点では、その恩恵についてはお伝えできませんが、 いずれ機会があれば、その結果もお伝えできればと思っています。 >スクリプトの処理が完了するのに、10秒かかるとのことですが 「exit;を途中に置いては、その変化を調べる」なんて方法で、 どの辺りの処理で手間取っているか簡単にチェックしてみました。 その結果、なんとなく予想していた箇所で、やはり手間取っていました。 その箇所を言葉で説明しますので、 処理速度を遅くしていると思われる点をご指摘頂けると嬉しいです。 では、説明致します。 処理のフローを箇条書きで書きます。 //-------------------------------- ・file_get_contents()にてファイル情報(かなり長いファイル)を取得 (ファイルサイズは、50KB~70KBくらいで、処理するスクリプトから見て、 外部サーバに置かれているファイルにアクセスして取得するカタチになります。) ・そのファイル情報の中から、正規表現を利用し、該当パターンのデータを抽出 ・抽出データ(正規表現のパターン)は複数あり、それぞれ同様に抽出(preg_match()使用) ・そのパターンをあらかじめ配列に入れておき、foreachで一括処理。 ・foreach内での一括処理では、あらかじめnewされてあるクラスを利用しています。 (正規表現をもとに、該当データをファイル情報の中から抽出するクラスを、 配列の要素の数だけ繰り返し使っているということです。 ※ちなみに、繰り返しnewしていたりはしません。) ・foreach内で、抽出されたデータは、変数にそれぞれ入れられていきます。 (echoや、DBへのインサートなどの処理はしていません。変数に格納するだけです。 また、抽出されるデータは、どれも短く、マルチバイト文字列ではありません。mb_pregではなく、pregを使用。) //-------------------------------- 処理はここまでです。 この部分だけで4~5秒掛かっています。 (前述のexit;による方法で確認) さて、どのあたりに問題はありそうでしょうか。 ファイルのコンテンツが多い(データサイズが大きい)点が怪しいでしょうか。 それとも、 正規表現のパターンを、より絞り込まれやすい(より具体的、より長い)にすべきなのでしょうか。 (一般的に、正規表現は、処理速度を遅くする原因になりがちですか?) ereg系よりpreg系の方が速いらしいので、この点はおそらくこれで問題ないでしょう・・・。 なお、この手間取っている処理の中では、データベースを扱った処理はありませんので、 DB系の手間取りが原因というわけではなさそうです。 以上、よろしくお願い致します。
- gwagaw
- ベストアンサー率18% (11/59)
No.1です。 よっぽどおかしいソースでも無い限り、気にすることもないと思います。 シンプルかつ洗練された、他人が見て感動するようなソースを心がけられるほうが結果的には良いのではないかなと思います。 自然無駄もなくなり処理も早くなります。 見た瞬間絶頂してしまうような、そんなソースができてしまったら。。。 是非私に見せてください。
お礼
>シンプルかつ洗練された、他人が見て感動するようなソースを心がけられるほうが結果的には良い スクリプトの処理速度を上げる方法には色々な視点がありますものね。 メモリ解放の他にもすべきことがないか、色々考えてみます。 >是非私に見せてください。 お母ちゃんに相談してみます。
- gwagaw
- ベストアンサー率18% (11/59)
unsetでいいんじゃないでしょうか? だた、メモリ管理が最大のクリティカル要因だと考えて居られるのでしたら、cでの実装を検討されることをお勧めします。 cがの実装が難しいようのでしたら、サーバーのスペックをあげてください。 またメモリ管理に躍起になり、ソース自体に悪い影響を与えないようにご注意ください。
お礼
特段、メモリ管理を意識しているわけではないのですが、 簡単な方法(ごく一般的な方法)で、 インスタンス化による、スクリプト処理速度の低下を防ぐことができるのであれば、 それは知っておいたほうがいいだろうなと思い、質問をさせて頂きました。 なお、話の順番が逆かもしれませんが、 そもそも、インスタンス化によるスクリプト処理速度低下が 一般的にどの程度のものなのかが私には分かっていませんので、 もしかしたら、私が作っているレベルのスクリプトであれば、 「クラスを利用していたとしても、それほど処理速度は低下しておらず、 ゆえに、unset処理すら必要ない」のかもしれません。 そんな私ですので、 C言語による実装がどれほど強力なのかについても、よく分かっていません。 (速くはなるだろうなー、くらいの認識です。) 正直、どの程度速くなるのか、気になる所ではあります。 (ただし、私にはCを操ることはできません。) 参考になる回答を、どうもありがとうございました。 助かります。
お礼
詳しく説明頂き、ありがとうございます。 現在作成しているスクリプトが、それほど複雑ではないにも関わらず、 処理に10秒くらいかかるので、なんでだろうと思っていました。 スクリプトの処理速度低下の原因を見つける方法で、 何かお勧めの方法はありますでしょうか。 やはり、microtime()を要所に仕掛け、 地道に探るしかないのでしょうか。