ホームページ >バックエンド開発 >PHPチュートリアル >PHP ソースコードの簡単な説明 34: PHP5.3 に新しく追加されたガベージ コレクション メカニズム (ガベージ コレクション)
この記事では主に PHP ソースコード 34: PHP5.3 で追加されたガベージコレクション機構 (Garbage Collection) について紹介します。必要な方は参考にしてください。
前回の記事では、PHP ソース コード 33: PHP5 について簡単に説明しました。 3 に新たに追加された「ガベージ コレクションの基礎」では、ガベージ コレクション メカニズムの基本的な知識を紹介します。今日は、その初期化、ガベージ バッファーとガベージ コレクション プロセスの追加について見ていきます。
公式ドキュメントについては、「ガベージ コレクション」をクリックしてください。
中国語版アドレス: http://docs.php.net/manual/zh/features.gc.php
[初期化]
zend/ zend_gc .c 121 行目には、gc の初期化を実装する関数 gc_init があります。コードは次のとおりです。
ZEND_API void gc_init(TSRMLS_D){ if (GC_G(buf) == NULL && GC_G(gc_enabled)) { GC_G(buf) = (gc_root_buffer*) malloc(sizeof(gc_root_buffer) * GC_ROOT_BUFFER_MAX_ENTRIES); GC_G(last_unused) = &GC_G(buf)[GC_ROOT_BUFFER_MAX_ENTRIES]; gc_reset(TSRMLS_C); }}123 行目は、それが空であるかどうか、および gc が有効であるかどうかを判断します。両方とも true の場合は、行に進みます。 124
行 124 は、malloc を直接呼び出して、10,000 gc_root_buffer メモリを割り当てます。
行 125 は、グローバル変数 last_unused を gc バッファの終了位置に設定します。
行 126 はガベージ コレクション メカニズム全体をリセットします。コードは次のように zend/zend_gc.c の行 88 から始まります。
ZEND_API void gc_reset(TSRMLS_D){ GC_G(gc_runs) = 0; GC_G(collected) = 0; #if GC_BENCH GC_G(root_buf_length) = 0; GC_G(root_buf_peak) = 0; GC_G(zval_possible_root) = 0; GC_G(zobj_possible_root) = 0; GC_G(zval_buffered) = 0; GC_G(zobj_buffered) = 0; GC_G(zval_remove_from_buffer) = 0; GC_G(zobj_remove_from_buffer) = 0; GC_G(zval_marked_grey) = 0; GC_G(zobj_marked_grey) = 0;#endif GC_G(roots).next = &GC_G(roots); GC_G(roots).prev = &GC_G(roots); if (GC_G(buf)) { GC_G(unused) = NULL; GC_G(first_unused) = GC_G(buf); GC_G(zval_to_free) = NULL; } else { GC_G(unused) = NULL; GC_G(first_unused) = NULL; GC_G(last_unused) = NULL; }}行 90 ~ 91 は gc の実行回数の統計を設定します ( gc_runs) と gc 内の (収集された) ガベージの数は 0 です。
106~107行目は、二重リンクリストの先頭ノードの前のノードと次のノードが自身を指すように設定しています。
実装コードは zend/zend.c の 93 行目にあります:
STD_ZEND_INI_BOOLEAN("zend.enable_gc","1",ZEND_INI_ALL,OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals)初期化呼び出しは zend/zend.c の 79 行目にあります
static ZEND_INI_MH(OnUpdateGCEnabled) /* {{{ */{ OnUpdateBool(entry, new_value, new_value_length, mh_arg1, mh_arg2, mh_arg3, stage TSRMLS_CC); if (GC_G(gc_enabled)) { gc_init(TSRMLS_C); } return SUCCESS;}[ガベージに追加バッファ]
PHP ソース コード zend/zend_execute_API.c 行 424
[_zval_ptr_dtor] -> [GC_ZVAL_CHECK_POSSIBLE_ROOT()] -> [gc_zval_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 && GC_ZVAL_ADDRESS(zv) != NULL && GC_ZVAL_GET_COLOR(zv) == GC_BLACK) && (GC_ZVAL_ADDRESS(zv) < GC_G(buf) || GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) { /* The given zval is a garbage that is going to be deleted by * currently running GC */ return; } if (zv->type == IS_OBJECT) { GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv); return; } GC_BENCH_INC(zval_possible_root); if (GC_ZVAL_GET_COLOR(zv) != GC_PURPLE) { GC_ZVAL_SET_PURPLE(zv); if (!GC_ZVAL_ADDRESS(zv)) { gc_root_buffer *newRoot = GC_G(unused); if (newRoot) { GC_G(unused) = newRoot->prev; } else if (GC_G(first_unused) != GC_G(last_unused)) { newRoot = GC_G(first_unused); GC_G(first_unused)++; } else { if (!GC_G(gc_enabled)) { GC_ZVAL_SET_BLACK(zv); return; } zv->refcount__gc++; gc_collect_cycles(TSRMLS_C); zv->refcount__gc--; newRoot = GC_G(unused); if (!newRoot) { return; } GC_ZVAL_SET_PURPLE(zv); GC_G(unused) = newRoot->prev; } newRoot->next = GC_G(roots).next; newRoot->prev = &GC_G(roots); GC_G(roots).next->prev = newRoot; GC_G(roots).next = newRoot; GC_ZVAL_SET_ADDRESS(zv, newRoot); newRoot->handle = 0; newRoot->u.pz = zv; GC_BENCH_INC(zval_buffered); GC_BENCH_INC(root_buf_length); GC_BENCH_PEAK(root_buf_peak, root_buf_length); } }}
行 132 ~ 140 で、zval ノード情報が存在するかどうかを確認します。ノード バッファに置かれている場合は、直接戻ります。これにより、パフォーマンスが最適化されます。
行 142 から 145 は、オブジェクト ノードを処理し、後続の操作を実行せずに直接戻ります。
行 149 は、ノードが紫色にマークされているかどうかを判断します。紫色の場合、ノードはノード バッファーに追加されなくなります。これは、ノードがバッファーに 1 回だけ追加されるようにするためです。
行 150 はノードの色を紫としてマークし、このノードがバッファに追加されたことを示し、次回追加する必要がないことを示します。
行 153 ~ 157 は、新しいノード バッファがいっぱいの場合、ガベージ コレクション操作が実行されます。
行 176 ~ 184 では、バッファが配置されている二重リンク リストに新しいノードを追加します。
[ガベージ コレクション プロセス]
gc_zval_possible_root 関数では、バッファーがいっぱいになると、プログラムは gc_collect_cycles 関数を呼び出してガベージ コレクション操作を実行します。 zend/zend_gc.c ファイルの 615 行目以降の実装コードは次のとおりです。ZEND_API int gc_collect_cycles(TSRMLS_D){ int count = 0; if (GC_G(roots).next != &GC_G(roots)) { zval_gc_info *p, *q, *orig_free_list, *orig_next_to_free; if (GC_G(gc_active)) { return 0; } GC_G(gc_runs)++; GC_G(zval_to_free) = FREE_LIST_END; GC_G(gc_active) = 1; gc_mark_roots(TSRMLS_C); gc_scan_roots(TSRMLS_C); gc_collect_roots(TSRMLS_C); orig_free_list = GC_G(free_list); orig_next_to_free = GC_G(next_to_free); p = GC_G(free_list) = GC_G(zval_to_free); GC_G(zval_to_free) = NULL; GC_G(gc_active) = 0; /* First call destructors */ while (p != FREE_LIST_END) { if (Z_TYPE(p->z) == IS_OBJECT) { if (EG(objects_store).object_buckets && EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid && EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0 && EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor && !EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called) { EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].destructor_called = 1; EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount++; EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.dtor(EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.object, Z_OBJ_HANDLE(p->z) TSRMLS_CC); EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount--; } } count++; p = p->u.next; } /* Destroy zvals */ p = GC_G(free_list); while (p != FREE_LIST_END) { GC_G(next_to_free) = p->u.next; if (Z_TYPE(p->z) == IS_OBJECT) { if (EG(objects_store).object_buckets && EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].valid && EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount <= 0) { EG(objects_store).object_buckets[Z_OBJ_HANDLE(p->z)].bucket.obj.refcount = 1; Z_TYPE(p->z) = IS_NULL; zend_objects_store_del_ref_by_handle_ex(Z_OBJ_HANDLE(p->z), Z_OBJ_HT(p->z) TSRMLS_CC); } } else if (Z_TYPE(p->z) == IS_ARRAY) { Z_TYPE(p->z) = IS_NULL; zend_hash_destroy(Z_ARRVAL(p->z)); FREE_HASHTABLE(Z_ARRVAL(p->z)); } else { zval_dtor(&p->z); Z_TYPE(p->z) = IS_NULL; } p = GC_G(next_to_free); } /* Free zvals */ p = GC_G(free_list); while (p != FREE_LIST_END) { q = p->u.next; FREE_ZVAL_EX(&p->z); p = q; } GC_G(collected) += count; GC_G(free_list) = orig_free_list; GC_G(next_to_free) = orig_next_to_free; } return count;}
行 625 ~ 627 は、ガベージ コレクション操作の数に 1 を加算し、フリー リストを初期化し、gc_active を1 はガベージ リターンが進行中であることを示します
ここの 628 行目 公式ドキュメントのアルゴリズムのステップ B として、アルゴリズムは深さ優先検索を使用して、すべての可能なルートを見つけた後、各変数コンテナ内の参照カウントを見つけます。同じ変数コンテナが 2 回減分されないように、1" ずつ減分されます。''、1 減らされた灰色でマークされています。
629 行目 これはアルゴリズムのステップ C です。アルゴリズムは再び深度を使用します。最初に各ルート ノードを検索し、各変数コンテナの参照カウントを確認します。参照カウントが 0 の場合、変数コンテナは白でマークされます。参照数が 0 より大きい場合は、参照カウントを 1 減らす操作が行われます。この時点で深さ優先検索の使用が再開され (つまり、参照カウントが 1 増加します)、その後、それらは再び黒でマークされます
行 630 のアルゴリズムの最後のステップ D、アルゴリズムはルートをトラバースします。バッファを作成してそこから変数コンテナのルート (zval ルート) を削除します。同時に、前のステップでホワイトマークを付けられた変数コンテナが存在するかどうかを確認します。
[gc_collect_cycles.] () -> gc_collect_roots() -> zval_collect_white() ] 白くマークされたノードがグローバル変数 zval_to_free リストに追加されることがわかります。このリストは
行 632 ~ 633 で使用されます。グローバル変数 free_list と next_to_free を対応する一時変数に保存し、最後に現在の状態に復元されます。
行 634 ~ 635 は、クリアする必要があるリスト、クリアする zval リスト、およびガベージ コレクションの動作ステータスは非アクティブです。
639 ~ 655 行目でデストラクタを初めて呼び出し、クリアされた変数の数をカウントします。
657 ~ 678 行目で変数をクリアします。
682 ~ 686 行目でメモリを解放します。行 687 ~ 689 は、ガベージ カウントの統計を処理し、free_list 変数と next_to_free 変数を復元します。
上記がこの記事の全内容です。お役に立てれば幸いです。その他の関連コンテンツについては、以下に注目してください。 PHP 中国語 Web サイト
PHP ソース コード 33 についての簡単な説明: PHP5.3 の新しいガベージ コレクション メカニズム (ガベージ コレクション) の基本
概要PHP ソース コード 32 についての説明 : PHP メモリ プールの emalloc/efree 層とヒープ層
# PHP ソース コード 29 についての簡単な説明 : の継承についてインターフェース####### ##
以上がPHP ソースコードの簡単な説明 34: PHP5.3 に新しく追加されたガベージ コレクション メカニズム (ガベージ コレクション)の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。