PHP はマネージド言語です。PHP プログラミングでは、プログラマーがメモリ リソースの割り当てと解放を手動で処理する必要はありません (C で PHP または Zend 拡張機能を作成する場合を除く)。つまり、PHP 自体がガベージ コレクション メカニズム (ガベージ コレクション) を実装しています。 PHP の公式 Web サイト (php.net) にアクセスすると、PHP5 の 2 つのブランチ バージョン (PHP5.2 と PHP5.3) が別々に更新されていることがわかります。これは、多くのプロジェクトでまだ PHP バージョン 5.2 を使用しており、バージョン 5.3 は 5.2 と完全に互換性がないためです。 PHP5.3 は PHP5.2 をベースに多くの改良が加えられており、その中でもガベージコレクションアルゴリズムは比較的大きな変更点です。この記事では、PHP5.2 と PHP5.3 のガベージ コレクション メカニズムについてそれぞれ説明し、この進化と改善が PHP を作成するプログラマーに与える影響と、注意を払う必要がある問題について説明します。 PHP変数と関連するメモリオブジェクトの内部表現 ガベージ コレクションは、最終的には変数とそれに関連付けられたメモリ オブジェクトに対する操作です。そのため、PHP のガベージ コレクション メカニズムについて説明する前に、まず PHP における変数とそのメモリ オブジェクトの内部表現 (C ソース コードでの表現) について簡単に説明します。 公式 PHP ドキュメントでは、PHP の変数をスカラー型と複合型の 2 つのカテゴリに分類しています。スカラー型にはブール型、整数型、浮動小数点型、文字列型が含まれ、複合型には配列、オブジェクト、リソース型が含まれます。NULL は、どの型にも分類されず、別のカテゴリであるという点で特別です。 これらの型はすべて、PHP では zval と呼ばれる構造体によって統一的に表されます。この構造体の名前は、PHP ソース コードでは「_zval_struct」です。 zval の具体的な定義は、PHP ソース コードの「Zend/zend.h」ファイルにあります。以下は、関連するコードの抜粋です。
PHP では、すべての変数の値を表すためにユニオン「_zvalue_value」が使用されます。ここでユニオンが使用される理由は、zval が一度に 1 つのタイプの変数しか表すことができないためです。 _zvalue_value には 5 つのフィールドしかありませんが、PHP には NULL を含めて 8 つのデータ型があります。では、PHP はどのようにして 5 つのフィールドを使用して 8 つの型を表現するのでしょうか。これは、フィールドを再利用することでフィールドの数を減らすという PHP の巧妙な設計です。たとえば、PHP では、ブール型、整数、リソース (リソースの識別子が格納されている限り) はすべて lval フィールドを通じて格納されます。dval は浮動小数点型を格納するために使用されます。str は文字列を格納します。ht は配列を格納します (PHP の配列は実際にはハッシュ テーブルであることに注意してください)。obj はオブジェクト型を格納します。すべてのフィールドが 0 または NULL に設定されている場合、PHP では NULL を意味するため、5 つのフィールドに 8 種類の値が格納されます。 現在の zval の値の型 (値の型は _zvalue_value) は、「_zval_struct」の型によって決まります。 _zval_struct は、C 言語における zval の特定の実装です。各 zval は変数のメモリ オブジェクトを表します。 _zval_struct には、値と型の他に、refcount__gc と is_ref__gc という 2 つのフィールドがあることがわかります。サフィックスから、これら 2 つはガベージ コレクションに関連していることがわかります。そうです、PHP のガベージ コレクションはこれら 2 つのフィールドに完全に依存します。このうち、refcount__gc は現在この zval を参照している変数がいくつあるかを示し、is_ref__gc は現在の zval が参照されているかどうかを示します。これはわかりにくいように思えます。これは、PHP の zval の「Write-On-Copy」メカニズムに関連しています。このトピックはこの記事の焦点ではないため、ここでは詳しく説明しません。読者は、refcount__gc フィールドの機能を覚えておくだけで十分です。 PHP5.2 のガベージ コレクション アルゴリズム - 参照カウント PHP5.2 で使用されているメモリ回復アルゴリズムは、有名な参照カウントです。このアルゴリズムの中国語訳は「参照カウント」です。その考え方は非常に直感的で簡潔です。各メモリ オブジェクトにカウンタが割り当てられます。メモリ オブジェクトが作成されると、カウンタは 1 に初期化されます (つまり、この時点でこのオブジェクトを参照する変数が常に存在します)。新しい変数がこのメモリ オブジェクトを参照するたびに、カウンタは 1 ずつ増加し、このメモリ オブジェクトを参照する変数が減るたびに、カウンタは 1 ずつ減少します。ガベージ コレクション メカニズムが動作しているときは、カウンタが 0 のすべてのメモリ オブジェクトが破棄され、それらが占有していたメモリが再利用されます。 PHP のメモリ オブジェクトは zval で、カウンターは refcount__gc です。 たとえば、次の PHP コードは、PHP5.2 カウンターがどのように動作するかを示しています (カウンター値は xdebug を通じて取得されます)。
参照カウントはシンプルで直感的であり、実装も簡単ですが、メモリ リークが起こりやすいという致命的な欠陥があります。循環参照がある場合、参照カウントによってメモリ リークが発生する可能性があることに気付いた友人も多いでしょう。たとえば、次のコード:
このコードは、まず配列 a を作成し、次に a の最初の要素が参照によって a を指すようにします。この時点で、a の zval の参照カウントは 2 になります。次に、変数 a を破棄します。この時点で、a が元々指していた zval の参照カウントは 1 ですが、次の図に示すように、循環的な自己参照が形成されるため、これ以上操作することはできません。 灰色の部分はもう存在しません。 before によって指される zval の参照カウントは 1 (HashTable の最初の要素によって参照される) であるため、この zval は GC によって破棄されず、メモリのこの部分はリークされます。 ここで指摘しておくべき重要なことは、PHP は変数シンボルをシンボル テーブルを通じて保存することです。グローバル シンボル テーブルがあり、配列やオブジェクトなどの各複合型には独自のシンボル テーブルがあります。したがって、上記のコードでは、a と a[0] は 2 つのシンボルですが、a はグローバル シンボル テーブルに保存され、a[0] は配列自体のシンボル テーブルに保存され、ここで a と a[0] は同じ zval を参照します (もちろん、シンボル a は後で破棄されました)。読者の皆さんには、シンボルと zval の関係に注目していただければ幸いです。 PHP が動的ページ スクリプトにのみ使用される場合、動的ページ スクリプトのライフ サイクルは非常に短く、スクリプトの実行時にすべてのリソースが解放されることが PHP によって保証されるため、このリークはそれほど重要ではない可能性があります。しかし、PHP は現在、単なる動的ページ スクリプト以上のものに発展しています。自動テスト スクリプトやデーモン プロセスなど、ライフサイクルが長いシナリオで PHP を使用する場合、複数のサイクルを経て蓄積されたメモリ リークが深刻な問題となる可能性があります。誇張ではありません。私がかつてインターンシップをした会社では、データ ストレージ サーバーとやり取りするために PHP で書かれたデーモン プロセスを使用していました。 参照カウントのこの欠陥のため、PHP5.3 ではガベージ コレクション アルゴリズムが改善されました。 PHP5.3 のガベージ コレクション アルゴリズム - 参照カウント システムにおける並行サイクル コレクション PHP5.3 のガベージ コレクション アルゴリズムは依然として参照カウントに基づいていますが、コレクションの基準として単純なカウントは使用されなくなりました。代わりに、IBM のエンジニアが論文「Concurrent Cycle Collection in Reference Counted Systems」で提案した同期コレクション アルゴリズムが使用されます。 このアルゴリズムは、論文が 29 ページにも及ぶことからもわかるように、かなり複雑なので、このアルゴリズムについて十分に議論するつもりはありません (また、議論する能力もありません)。興味のある方は、上記の論文をお読みください (非常に興味深い論文なので、ぜひお読みください)。 ここでは、このアルゴリズムの基本的な考え方を大まかにしか説明できません。 まず、PHP は固定数の zval を格納するために使用される固定サイズの「ルート バッファ」を割り当てます。デフォルト値は 10,000 です。これを変更する必要がある場合は、ソース コード Zend/zend_gc.c の定数 GC_ROOT_BUFFER_MAX_ENTRIES を変更して再コンパイルする必要があります。 上記から、zval に参照がある場合、グローバル シンボル テーブル内のシンボルによって参照されるか、複合型を表す別の zval 内のシンボルによって参照されることがわかります。したがって、zval には複数のルートが考えられます。ここでは、PHP がこれらの可能なルートをどのように検出するかについては説明しません。これは非常に複雑な問題です。簡単に言うと、PHP にはこれらの可能なルート zval を検出し、ルート バッファーに配置する方法があります。 ルート バッファがいっぱいになると、PHP はガベージ コレクションを実行します。リサイクル アルゴリズムは次のとおりです。 1. ルート バッファ内の各ルート zval について、深さ優先のトラバース アルゴリズムに従ってトラバースできるすべての zval をトラバースし、各 zval の参照カウントを 1 減らします。同時に、同じ zval が 1 ずつ複数回減らされることを回避するために (異なるルートが同じ zval にトラバースする可能性があるため)、zval が 1 ずつ減らされるたびに、「減らされた」とマークされます。 2. 再度、各バッファのルート zval を深さ優先でトラバースします。zval の参照カウントが 0 でない場合は 1 を追加し、そうでない場合は 0 のままにします。 3. ルート バッファ内のすべてのルートをクリアします (これらの zval は破棄されるのではなく、バッファからクリアされることに注意してください)。次に、refcount 0 のすべての zval を破棄して、メモリを再利用します。 完全に理解していなくても問題ありません。PHP5.3 のガベージ コレクション アルゴリズムには次の特徴があることを覚えておいてください。 1. リサイクル サイクルは、refcount が減少するたびに開始されるわけではありません。ガベージ コレクションは、ルート バッファーがいっぱいになったときにのみ開始されます。 2. 循環参照の問題を解決できます。 3. メモリ リークは常にしきい値以下に抑えられます。 PHP5.2 と PHP5.3 のガベージ コレクション アルゴリズムのパフォーマンス比較 現在の制限により、実験を再設計することはせず、PHP マニュアルの実験を直接引用します。 2 つのパフォーマンスの比較については、PHP マニュアルの関連章を参照してください: http://www.php.net/manual/en/features.gc.performance-considerations.php。 まずメモリ リーク テストです。以下は、PHP マニュアルの実験コードとテスト結果チャートからの直接引用です。
累積的なメモリ リークが発生する可能性があるシナリオでは、PHP5.2 では累積的なメモリ リークが継続しますが、PHP5.3 では常にメモリ リークをしきい値 (ルート バッファー サイズに関連) 以下に制御できることがわかります。 さらに、パフォーマンスの比較もあります。
このスクリプトは、比較するのに十分な遅延時間を確保するために 1,000,000 サイクルを実行します。 次に、CLI を使用して、メモリのリサイクルをオン/オフにしてこのスクリプトを実行します。
私のマシンでは、実行時間はそれぞれ 6.4 秒と 7.2 秒でした。PHP5.3 のガベージ コレクション メカニズムが遅いことがわかりますが、影響は大きくありません。 ガベージコレクションアルゴリズムに関連する PHP 設定 php.ini の zend.enable_gc を変更するか、gc_enable() または gc_disable() を呼び出すことによって、PHP のガベージ コレクション メカニズムをオンまたはオフにすることができます。 PHP5.3 では、ガベージ コレクション メカニズムがオフになっている場合でも、PHP は可能なルート バッファを記録します。ただし、ルート バッファがいっぱいになると、PHP は自動的にガベージ コレクションを実行しません。もちろん、gc_collect_cycles() 関数を手動で呼び出すことで、いつでもメモリ コレクションを強制できます。 オリジナルリンク: http://www.cnblogs.com/leoo2sk/archive/2011/02/27/php-gc.html 【編集者のおすすめ】
|
<<: 面接中にアルゴリズムの質問を解く際にプログラマーが知っておくべきこと
>>: JavaScript におけるいくつかの一般的なソートアルゴリズムの共有
携帯電話の発表会を見れば、AI機能の追加が目に入ります。しかし、多くのユーザーはこれをやや否定的に捉...
実行している機械学習の種類に応じて、モデルのパフォーマンスを測定するために使用できるメトリックは多数...
チップを作る上で最も重要な部分は何ですか? より高度な製造プロセスを使用してトランジスタ密度と計算能...
1. 要件の説明長い文字列と短い文字列が与えられた場合、短い文字列のすべての文字が長い文字列に含まれ...
多くの企業は、事業運営において専門的なアドバイスを得るためにコンサルタントに依存しており、コンサルテ...
この記事はAI新メディアQuantum Bit(公開アカウントID:QbitAI)より許可を得て転載...
先週の発表に続き、OpenAI は本日、GPT ストアの立ち上げを正式に発表しました。写真昨年 11...
翻訳者|朱 仙中レビュー | Chonglou導入大規模言語モデル (LLM) が世界を席巻するにつ...
この記事は、Heart of Autonomous Driving の公開アカウントから許可を得て転...
長年にわたる技術の進歩により、交通はより便利になりました。 IoT アプリケーションなどの自動車技術...
大規模なモデルを微調整するための「無料ランチ」ができました。たった 1 行のコードで、パフォーマンス...
[[246531]]情報イラスト。出典:新華網ハリウッド映画「アベンジャーズ3」では、悪役サノスが「...
「平常時に努力しなければ、試験では友達に頼らざるを得なくなる」ということわざがある。試験が近づくに...