ホームページ  >  記事  >  バックエンド開発  >  PHP ソースコードの簡単な説明 34: PHP5.3 に新しく追加されたガベージ コレクション メカニズム (ガベージ コレクション)

PHP ソースコードの簡単な説明 34: PHP5.3 に新しく追加されたガベージ コレクション メカニズム (ガベージ コレクション)

不言
不言オリジナル
2018-06-29 10:07:01999ブラウズ

この記事では主に PHP ソースコード 34: PHP5.3 で追加されたガベージコレクション機構 (Garbage Collection) について紹介します。必要な方は参考にしてください。

# 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行目は、二重リンクリストの先頭ノードの前のノードと次のノードが自身を指すように設定しています。

gc_enabled については、デフォルトで有効になっており、php.ini で設定できます。

実装コードは 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;}

619 行目は、バッファーが空かどうかを判断します。空の場合、ガベージ コレクション操作は実行されません。

行 622 は、ガベージ コレクション操作が定期的に実行されるかどうかを決定します。実行中の場合は、直接戻ります。

行 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 サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。