typedef struct _zend_gc_globals {
zend_bool gc_enabled ; /* ガベージ コレクション メカニズムを有効にするかどうか*/
zend_bool gc_active; /* 進行中かどうか*/
gc_root_buffer *buf; /* 事前に割り当てられたバッファ配列、デフォルトは 10000 (事前に割り当てられたバッファの配列) * /
gc_root_bufferroots; /* リストのルート ノード (サイクルの可能なルートのリスト) */
gc_root_buffer *unused; /* 未使用のバッファのリスト (未使用のバッファのリスト) */
gc_root_buffer *first_unused;最初の未使用バッファ ノードを指します (最初の未使用バッファへのポインタ) */
gc_root_buffer *last_unused; /* 最後の未使用バッファ ノードを指します。ここがマークの終わりです (最後の未使用バッファへのポインタ) * /
zval_gc_info *zval_to_free; /* 解放する zval の一時リスト */
zval_gc_info *free_list; /* 一時変数、解放する必要があるリストの先頭 */
zval_gc_info *next_to_free; /* 一時変数、次の変数の位置解放される変数*/
zend_uint gc_runs; /* gc の実行回数の統計*/
zend_uint が収集した数*/
// 省略...
}
unset 操作を使用してこの変数が占有しているメモリをクリアすると (おそらく参照カウントを 1 つ減らすだけです)、すべての操作が実行された後、変数名に対応する項目が現在のシンボルのハッシュ テーブルから削除されます。 、および デストラクターはシンボル テーブルから削除された項目に対して呼び出され、zval_dtor は一時変数に対して呼び出され、zval_ptr_dtor は通常の変数に対して呼び出されます。
もちろん、unset 関数は言語構造であるため、PHP の関数セットで見つけることはできません。 対応する中間コードは ZEND_UNSET で、関連する実装は Zend/zend_vm_execute.h ファイルにあります。
zval_ptr_dtor は関数ではなく、関数に少し似た単なるマクロです。 Zend/zend_variables.h ファイルでは、このマクロは関数 _zval_ptr_dtor を指します。 ZEND /ZEND_EXECUTE_API.C 424 行の関数関連のコードは次のとおりです。
コードをコピー コードは次のとおりです。
zend_api void /
{i#if DEBUG_ZEND>=2
printf("Reducing refcount %x (%x) の場合: %d->%dn", *zval_ptr, zval_ptr, Z_REFCOUNT_PP(zval_ptr), Z_REFCOUNT_PP(zval_ptr) - 1);
#endif
Z_DELREF_PP (zval_ptr);
if (Z_REFCOUNT_PP(zval_ptr) ) == 0) {
TSRMLS_FETCH();
if (*zval_ptr != &EG(uninitialized_zval)) {
GC_REMOVE_ZVAL_FROM_BUFFER(*zval_ptr);
efree_rel (*zval_ptr);
}
} else {
TSRMLS_FETCH();
if (Z_REFCOUNT_PP(zval_ptr) == 1) {
Z_UNSET_ISREF_PP(zval_ptr)
}
} ; /* }}} */
からこのコードでは、この zval の破棄プロセスが明確にわかります。次の 2 つの操作が参照カウント フィールドに対して実行されます。
変数の参照カウントが 1 の場合、つまり、参照カウントが 1 つ減ります。 0 の場合、変数は直接クリアされます。現在の変数がキャッシュされている場合、変数の参照カウントが 1 より大きい場合、つまり、1 を引いた後の参照カウントが 0 より大きい場合、変数はガベージ リストに入れられます。変更に参照がある場合は、その参照を削除します。
変数をガベージ リストに入れる操作は GC_ZVAL_CHECK_POSSIBLE_ROOT で、これもマクロで関数 gc_zval_check_possible_root に相当しますが、この関数は配列とオブジェクトに対してガベージ コレクション操作のみを実行します。配列変数とオブジェクト変数の場合は、gc_zval_possible_root 関数を呼び出します。
コードをコピーします
コードは次のとおりです:
ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC) { if (UNEXPECTED(GC_G(free_list) != NULL && ESS(zv ) != NULL && GC_ZVAL_GET_COLOR( zv) == GC_BLACK) &&
(GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||
GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {
/* 指定された zval はガベージです。
* 現在実行中の GC */
return;
if (zv->type == IS_OBJECT) {
GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv)
GC_BENCH_INC(zval_possible_root);
場合( GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) {
GC_ZVAL_SET_PURPLE(zv);
if (!GC_ZVAL_ADDRESS(zv)) {
gc_root_buffer *newRoot = GC_G(未使用)
if (newRoot) {
GC_G(unused); - >前;
} else if (GC_G(first_unused) != GC_G(last_unused)) {
newRoot = GC_G(first_unused)++
} else {
if (!GC_G(gc_enabled)) {
GC_ZVAL_SET_BLACK(zv);
}
zv->refcount__gc++;
newRoot = GC_G(未使用); !newRoot) {
return;
}
GC_ZVAL_SET_PURPLE(zv);
GC_G(未使用) = newRoot->prev;
newRoot->prev = &GC_G(roots);
GC_G(ルート).next->prev = newRoot;
GC_ZVAL_SET_ADDRESS(zv, newRoot);
newRoot->u.pz; zv;
GC_BENCH_INC(root_buf_length);
GC_BENCH_PEAK(root_buf_peak, root_buf_length);
前述したように、gc_zval_check_possible_root 関数は配列とオブジェクトに対してのみガベージ コレクション操作を実行します。ただし、gc_zval_possible_root 関数では、オブジェクト型の変数に対して GC_ZOBJ_CHECK_POSSIBLE_ROOT マクロが呼び出されます。ガベージ コレクション メカニズムに使用できる他の変数タイプの場合、呼び出しプロセスは次のとおりです。
zval ノード情報がノード バッファーに入れられているかどうかを確認し、ノード バッファーに入れられている場合は、直接戻ります。パフォーマンスを最適化します。 次に、オブジェクト ノードを処理し、その後の操作を実行せずに直接戻り、ノードが紫色にマークされているかどうかを確認します。これは、ノードが 1 回だけ追加されるようにするためです。 . バッファー操作。
ノードの色を紫としてマークします。これは、ノードがバッファに追加されており、次回追加する必要がないことを示します。
バッファがいっぱいの場合は、ガベージ コレクションを実行します。 。
バッファが配置されている二重リンクリストに新しいノードを追加します。
gc_zval_possible_root 関数では、バッファーがいっぱいになると、プログラムは gc_collect_cycles 関数を呼び出してガベージ コレクション操作を実行します。
最も重要なステップは :
628 行目は、公式ドキュメントのアルゴリズムのステップ B であり、アルゴリズムは深さ優先検索を使用してすべての可能なルートを見つけた後、各変数コンテナーの参照カウントが 1 ずつ減らされます。 . 同じ変数コンテナが 2 回「1」減らされないように、1 減分されたものを灰色でマークします。
629 行目 これはアルゴリズムのステップ C で、アルゴリズムは再び各ルート ノードに対して深さ優先検索を使用し、各変数コンテナの参照カウントをチェックします。 参照カウントが 0 の場合、変数コンテナーは白でマークされます。参照カウントが 0 より大きい場合は、深さ優先検索を使用してこの時点で参照カウントを減分 (つまり、参照カウントを 1 ずつ増やし) した操作を再開し、それらを黒で再マークします。
630 行目 アルゴリズムの最後のステップ D では、アルゴリズムはルート バッファーを走査してそこから変数コンテナー ルート (zval ルート) を削除し、同時に前のステップで白とマークされた変数コンテナーがあるかどうかを確認します。 。白でマークされた各変数コンテナはクリアされます。 [gc_collect_cycles() -> gc_collect_roots() -> zval_collect_white() ] では、白でマークされたノードがグローバル変数 zval_to_free リストに追加されることがわかります。このリストは後で使用します。
PHP のガベージ コレクション メカニズムは、実行中にステータスを 4 色でマークします。
GC_WHITE 白はゴミを意味します
GC_PURPLE 紫はバッファに入れられたことを意味します
GC_GREY 灰色は 1 を減らすために refcount 操作が実行されたことを意味します
GC_BLACK 黒はデフォルトの色、通常です
関連するマークと操作コードは次のとおりです:
コードをコピーします コードは次のとおりです:
#define GC_COLOR 0x03
#define GC_BLACK 0x00
#define GC_WHITE 0x01
#define GC_GREY 0x02
#define GC_PURP LE 0x03
#define GC _ADDRESS(v)
( (gc_root_buffer*)((zend_uintptr_t) (v)) & ~GC_COLOR))
#define GC_SET_ADDRESS(v, a)
(v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & GC_COLOR) | ((zend_uintptr_t)(a)) ))
#define GC_GET_COLOR(v)
(((zend_uintptr_t)(v)) & GC_COLOR)
#define GC_SET_COLOR(v, c)
(v) = ((gc_root_buffer*)( (((zend_uintptr_t)(v) ) & ~GC_COLOR) (c)))
#define GC_SET_BLACK(v)
(v) = ((gc_root_buffer*)((zend_uintptr_t)(v)) & ~GC_COLOR))
#define GC_SET_PURPLE(v)
(v) = ((gc_root_buffer*)((zend_uintptr_t)(v)) | GC_PURPLE))
ステータスをビットでマークする上記の方法は、次のような PHP ソース コードでより頻繁に使用されます。これは、より効率的で経済的なソリューションです。ただし、データベースを設計するときは、このメソッドをフィールドに使用できない場合があります。より直感的で読みやすい方法で実装する必要があります。
http://www.bkjia.com/PHPjc/326234.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/326234.html技術記事ガベージ コレクション メカニズムは、動的なストレージ割り当てスキームです。プログラムで不要になった割り当てられたメモリ ブロックを自動的に解放します。 メモリを自動的に再利用するプロセスは、ガベージ コレクションと呼ばれます。ガベージ コレクション メカニズムは...