従来、PHP で使用される参照カウント メモリ メカニズムは、循環参照メモリ リークを処理できませんでした。ただし、5.3.0 PHP は、このメモリ リークの問題に対処するために、記事「参照カウント システムの同時サイクル コレクション」にある同期アルゴリズムを使用します。
アルゴリズムの完全な説明はこのセクションの範囲を少し超えるため、基本的な部分のみを紹介します。まず、いくつかの基本的なルールを確立する必要があります。参照カウントが増加した場合、その参照カウントは引き続き使用され、当然ゴミには入れられなくなります。参照カウントがゼロになると、変数コンテナはクリア (解放) されます。つまり、ガベージ サイクルは、参照カウントがゼロ以外の値に減少した場合にのみ発生します。次に、ガベージ サイクル中に、参照カウントが 1 減らされているかどうかを確認し、どの変数コンテナの参照がゼロであるかを確認して、どの部分がガベージであるかを見つけます。
参照カウントが削減される可能性があるすべてのガベージ サイクルをチェックする必要を避けるために、このアルゴリズムはすべての可能なルート (可能なルートは zval 変数コンテナー) をルート バッファー (紫色でマーク) に配置します。可能性のあるガベージ ルートはバッファ内に 1 回だけ出現します。ガベージ コレクションは、ルート バッファーがいっぱいの場合にのみ、バッファー内のすべての異なる変数コンテナーに対して実行されます。上の画像のステップ A を見てください。
ステップ B では、アルゴリズムは深さ優先検索を使用して、考えられるすべてのルートを見つけた後、同じ変数コンテナーが「1」減らされないように、各変数コンテナーの参照カウントを「1」ずつ減分します。 「」を2回、「1」減算したものは灰色で表示されます。ステップ C では、アルゴリズムは再び各ルート ノードに対して深さ優先検索を使用し、各変数コンテナの参照カウントをチェックします。参照カウントが 0 の場合、変数コンテナは白 (図では青) でマークされます。参照カウントが 0 より大きい場合は、この時点で深さ優先検索を使用して参照カウントを "1" 減分 (つまり、参照カウントを "1" ずつ増加) した操作を再開し、それらを再マークします。黒で。最後のステップ D では、アルゴリズムはルート バッファーを走査してそこから変数コンテナー ルート (zval ルート) を削除し、同時に、前のステップで白とマークされた変数コンテナーがあるかどうかを確認します。白でマークされた各変数コンテナはクリアされます。
このアルゴリズムの基本を理解したところで、戻ってこれがどのように PHP と統合されるかを見てみましょう。デフォルトでは、PHP のガベージ コレクション メカニズムはオンになっており、それを変更できる php.ini 設定 (zend.enable_gc) があります。
ガベージコレクションメカニズムがオンになっている場合、ルートバッファがいっぱいになるたびに、上記のループ検索アルゴリズムが実行されます。ルート キャッシュ領域は固定サイズで、10,000 個のルートを保存できます。もちろん、PHP ソース ファイル Zend/zend_gc.c 内の定数 GC_ROOT_BUFFER_MAX_ENTRIES を変更して PHP を再コンパイルすることで、この 10,000 個の値を変更できます。ガベージ コレクションがオフになっている場合、ループ検索アルゴリズムは実行されませんが、構成でガベージ コレクションがアクティブになっているかどうかに関係なく、ルートが常にルート バッファーに存在する可能性があります。
ガベージ コレクション メカニズムがオフになっている場合、ルート バッファーがルート候補でいっぱいの場合、明らかにそれ以上のルートは記録されません。記録されていないルートの可能性は、このアルゴリズムでは分析および処理されません。それらが循環参照サイクルの一部である場合、それらは決してクリアされず、メモリ リークの原因となります。
ガベージ コレクションが利用できない場合でもルート候補が記録される理由は、ルート候補を記録する方が、ルート候補が見つかるたびにガベージ コレクションがオンになっているかどうかを確認するよりも高速であるためです。ただし、ガベージ コレクションと分析のメカニズム自体には多くの時間がかかります。
zend.enable_gc 設定の変更に加えて、gc_enable() 関数と gc_disable() 関数をそれぞれ呼び出して、ガベージ コレクション メカニズムをオンまたはオフにすることもできます。これらの関数を呼び出すと、構成項目を変更してガベージ コレクション メカニズムをオンまたはオフにするのと同じ効果があります。ルート バッファーがいっぱいでない場合でも、定期的な収集を強制する機能。この目的のために、 gc_collect_cycles() 関数を呼び出すことができます。この関数は、このアルゴリズムを使用してリサイクルされたサイクル数を返します。
ガベージ コレクションのオンとオフを切り替えたり、自律的な初期化を許可したりする理由は、アプリケーションの一部の部分が時間に依存する可能性があるためです。この場合、おそらくガベージ コレクションを使用したくないでしょう。もちろん、アプリケーションの特定の部分のガベージ コレクションをオフにすると、限られたルート バッファーに収まらないルートが存在する可能性があるため、メモリ リークが発生する可能性があります。したがって、gc_disable() 関数を呼び出してメモリを解放する直前に、最初に gc_collect_cycles() 関数を呼び出すことが賢明です。これにより、ルート バッファーに格納されているすべての可能なルートがクリアされるため、ガベージ コレクション メカニズムがオフになったときに、空のバッファーを残して、ルート バッファーに格納できるスペースを増やすことができます。