PHPゴミ回収の理解

3093 ワード

PHPタイプ
PHPの8種類のデータ型はクリックして見ることができます.
ソース表示
struct _zval_struct {  
    /* Variable information */ 
    zvalue_value value;       
    /* value */ 
    zend_uint refcount__gc;  
    zend_uchar type;    /* active type */ 
    zend_uchar is_ref__gc;  
}; 

ここでzvalue_valueは次のとおりです.
typedef union _zvalue_value {  
    long lval;                  /* long value */ 
    double dval;                /* double value */ 
    struct {  
        char *val;  
        int len;  
    } str;  
    HashTable *ht;              /* hash table value */ 
    zend_object_value obj;  
} zvalue_value;  

ここでrefcount_gcは、現在いくつかの変数がこのzvalを参照していることを示し、is_ref__gcは現在のzvalが参照によって引用されているかどうかを示すが,これはPHPにおけるzvalの「Write-On-Copy」メカニズムと関係がある.
PHP5.2のゴミ回収アルゴリズム――Reference Counting
PHP5.2で使用されるメモリ回収アルゴリズムは有名なReference Countingで、このアルゴリズムの中国語翻訳は「参照カウント」と呼ばれ、その思想は非常に直観的で簡潔である:メモリオブジェクトごとにカウンタを割り当て、メモリオブジェクトが確立されるとカウンタを1に初期化する(そのため、このオブジェクトを参照する変数が常にある).その後、新しい変数がこのメモリオブジェクトを参照するたびにカウンタは1を加算し、メモリオブジェクトを参照する変数を1つ減らすたびにカウンタは1を減算し、ゴミ回収メカニズムが動作すると、すべてのカウンタが0のメモリオブジェクトを破棄し、使用したメモリを回収します.PHPのメモリオブジェクトはzvalで、カウンタはrefcount_です.gc.
例えば、次のPHPコードについてPHP 5を示す.2カウンタの動作原理(カウンタ値はxdebugで得られる):
//zval(val1).refcount_gc = 1;
$val1 = 100;   
//zval(val1).refcount_gc = 2,zval(val2).refcount_gc = 2(   Write on copy,  val2 val1      zval) 
$val2 = $val1;  
//zval(val1).refcount_gc = 1,zval(val2).refcount_gc = 1(  val2     zval) 
$val2 = 200;  
//zval(val1).refcount_gc = 0($val1   zval     ,  GC  )  
unset($val1); 

メモリリーク
$a = array();  
$a[] = & $a;  
unset($a); 

このコードは、まず配列aを確立し、aの最初の要素を参照によってaを指すようにします.このとき、aのzvalのrefcountは2になり、変数aを破棄します.このとき、aが最初に指すzvalのrefcountは1になりますが、ループ自己参照を形成しているため、操作することはできません.aの前に指すzvalのrefcountが1(HashTableの最初の要素に参照される)であるため、このzvalはGCによって破棄されず、メモリの一部が漏れてしまう.
PHP5.3回収アルゴリズムの改善
PHP5.3のごみ回収アルゴリズムは依然として参照カウントに基づいているが,単純カウントを回収準則として用いるのではなく,IBMのエンジニアが論文Concurrent Cycle Collection in Reference Counted Systemsで提案した同期回収アルゴリズムを用いた.
このアルゴリズムはかなり複雑で、このアルゴリズムの基本思想を大体説明するしかない.
まずPHPは固定サイズの「ルートバッファ」を割り当てます.このバッファは固定数のzvalを格納するために使用されます.この数はデフォルトで10000です.変更が必要な場合はソースコードZend/zend_を変更する必要があります.gc.cの定数GC_ROOT_BUFFER_MAX_ENTRIESは再コンパイルされます.
前述したように、1つのzvalが参照されている場合、グローバルシンボルテーブルのシンボルによって参照されるか、複雑なタイプを表す他のzvalのシンボルによって参照されるかを知ることができます.したがってzvalにはいくつかの可能なルート(root)が存在する.ここではPHPがこれらの可能なルートをどのように発見したのかはしばらく議論しないが,これは複雑な問題であり,要するにPHPはこれらの可能なルートzvalを発見し,ルートバッファに投入する方法がある.
ルートバッファが満額の場合、PHPはゴミ回収を実行します.この回収アルゴリズムは以下の通りです.
1、各ルートバッファ内のルートzvalに対して深さ優先遍歴アルゴリズムに従って遍歴できるすべてのzvalを遍歴し、各zvalのrefcountを1減少させるとともに、同じzvalに対して複数回1減少しないように(異なるルートが同じzvalに遍歴できる可能性があるため)、あるzvalに対して1減少するたびに「減少」とマークする.
2、各バッファ内のルートzvalの深さを再び優先的に遍歴し、あるzvalのrefcountが0でない場合は1を加算し、そうでない場合は0を維持します.
3、ルートバッファ内のすべてのルートをクリアし(これらのzvalをバッファから消去し、破棄するのではなくクリアすることに注意)、refcountが0のzvalをすべて破棄し、メモリを回収します.
完全に理解できなくても大丈夫、PHP 5を覚えるだけです.3のごみ回収アルゴリズムには以下の特性がある.
1、refcountが減少するたびに回収サイクルに入るわけではなく、ルートバッファが満額になった後にゴミ回収を開始するだけです.
2、循環引用問題を解決できる.
3、メモリ漏洩は常に1つの閾値以下に保つことができる.