前回は refcount と is_ref について話しましたが、ここではメモリ リークについて話しましょう
$a = array(1, 2, &$a);
unset($ a);
古いPHPバージョンでは、ここでメモリリークが発生します。 解析は以下の通りです:
最初の行を実行すると、zval refcount=2とis_ref=1が$aと$a[2]で指されていることが分かります
次に2行目を実行すると、$aがシンボルテーブルから削除され、同時にzvalのrefcount--を指します。このとき、refcount!=0なので、zvalはrefcount=1になります。ガベージコレクションとして扱われませんが、このとき、このzvalを指す$a[2]のエントリが失われ、このzvalはメモリのゴミとなってしまいました
同じことがクラスの内部参照でも起こります。たとえば
$a = new Man();
$a->self = &$a ;
unset ($a);
では、この問題を解決するには、新しい GC メカニズムがアルゴリズムを使用します
PHP には、zval ノード情報を保存するためのルート バッファーがあり、ルート バッファーがいっぱいになるか、gc 関数が手動で呼び出されると、GC アルゴリズムが開始されます
。配列またはクラス型 zval の場合、ガベージ コレクション メカニズムが開始されると、アルゴリズムはクラス内の zval 配列/要素/メンバーの zval を走査し、refcount を 1 ずつ減分します。 zval が 0 に減らされるということは、zval がメモリのゴミであり、破棄されることを意味します。以下の例を参照してください
$a = array(1) , 2, &$a, &$a);
unset($a);
z1のrefcount=3、is_ref=1と仮定すると、$aが指すzvalを知るのは簡単です
unset($a)を実行すると、シンボルテーブルから$aが削除され、z1へのアクセスも失われます
。GC が開始されると、z1 の配列要素の zval の refcount がトラバースされて 1 減算されます。 a[2] にトラバースする場合は z1 refcount--、a[3] が z1 refcount-- の場合、この時点で時間 z1 refcount = 0、z1 はメモリのゴミとしてマークでき、アルゴリズムの後にリサイクルされます
要約すると、次のように表現できます: 配列型 zval がある場合、その要素 zval が 1 回走査され、refcount=0 の最後の zval がガベージである場合、走査された zval-- の refcount が使用されます。リサイクルする必要があります