- struct _zval_struct {
- /* 変数情報 */
- zvalue_value value; /* value */
- zend_uint refcount__gc;
- zend_uchar type; /* アクティブな型 */
- zend_uchar is_ref__gc;
- };
-
コードをコピー
PHP5.3より前のバージョンと比較すると、新しいガベージコレクション機構のために、参照カウントフィールドrefcountと参照フィールドis_refの後に__gcが追加されています。 PHP のソース コード スタイルでは、多数のマクロが非常に特徴的です。これらのマクロは、インターフェイス層に相当し、ALLOC_ZVAL マクロなど、インターフェイス層の下にある一部の実装を保護します。このマクロは、PHP のメモリ管理割り当て関数 emalloc を直接呼び出してメモリを割り当てていました。 by 変数の型とサイズが決定されます。 ガベージ コレクション メカニズムの導入後、ALLOC_ZVAL マクロは新しいガベージ コレクション ユニット構造を直接採用します。割り当てられるサイズはすべて同じであり、メモリを割り当てた後、zval_gc_info 構造によって占有されるメモリ サイズになります。この構造体は初期化されます。
- /* 次のマクロは、zend_alloc.h のマクロをオーバーライドします */
- #undef ALLOC_ZVAL
- #define ALLOC_ZVAL(z)
- do {
- (z) = (zval*)emalloc(sizeof(zval_gc_info));
- GC_ZVAL_INIT(z);
- } while (0)
-
コード
zend_gc.h ファイルは zend.h の 749 行目で参照されています: #include "zend_gc.h" により、237 行目で参照されている ALLOC_ZVAL が置き換えられますzend_alloc.h ファイル内の他のマクロ 新しいマクロでは、割り当てられたメモリ サイズと割り当て内容が変更され、以前の純粋なメモリ割り当てにすべての内容が追加されました。 zval_gc_info 構造内:
typedef struct _zval_gc_info {- zval z;
- union {
- gc_root_buffer *buffered;
- struct _zval_gc_info *next;
- } u;
- } コピーコード
-
-
ZVAL コンテナストレージの場合variable には zval 構造体が割り当てられます。これにより、zval 変数で割り当てられたメモリの先頭と確実に位置合わせされ、zval_gc_info 型ポインタがキャストされるときに zval として使用できるようになります。 zval フィールドの後に共用体があります: u。 u には、gc_root_buffer 構造体のバッファリングされたフィールドと zval_gc_info 構造体の次のフィールドが含まれます。 これら 2 つのフィールドの 1 つは、ガベージ コレクション メカニズムによってキャッシュされたルート ノードを表し、もう 1 つは、ガベージ コレクション メカニズムによってキャッシュされたノードがルート ノードとして使用されるかリスト ノードとして使用されるかに関係なく、zval_gc_info リスト内の次のノードを表します。ここに反映されます。 ALLOC_ZVAL は、zval を置き換える zval_gc_info を初期化するためにメモリを割り当てた後、GC_ZVAL_INIT を呼び出します。このフィールドは、ガベージ コレクション バッファーに配置される場合にのみ値を持ちます。それ以外の場合は、常に値が保持されます。 NULL にしてください。 PHP のすべての変数は zval 変数の形式で存在するため、ここでは zval_gc_info を使用して zval を置き換えることにより、ガベージ コレクション メカニズムを元のシステムに正常に統合できます。
PHP のガベージ コレクション メカニズムは PHP5.3 ではデフォルトで有効になっていますが、設定ファイルを通じて直接無効に設定できます。対応する設定フィールドは zend.enable_gc です。 デフォルトでは、php.ini ファイルにこのフィールドはありません。この機能を無効にする必要がある場合は、php.ini に zend.enable_gc=0 または zend.enable_gc=off を追加します。 php.ini 設定 zend.enable_gc を変更することに加えて、gc_enable()/gc_disable() 関数を呼び出してガベージ コレクション メカニズムをオン/オフにすることもできます。 これらの関数を呼び出す効果は、構成項目を変更してガベージ コレクション メカニズムをオンまたはオフにするのと同じです。 これら 2 つの関数に加えて、PHP にはルート バッファがいっぱいでない場合にサイクル リサイクルを強制する gc_collect_cycles() 関数が用意されています。 PHP ソース コードには、ガベージ コレクション メカニズムがオンになっているかどうかに関連する操作とフィールドがいくつかあります。
zend.c ファイルには次のコードがあります。
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;
- }
- /* }}} */
- ZEND_INI_BEGIN()
- ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting)
- STD_ZEND_INI_BOOLEAN("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals)
- #ifdef ZEND_MULTIBYTE
- STD_ZEND_INI_BOOLEAN("detect_unicode", "1", ZEND_INI_ALL, OnUpdateBool, detect_unicode, zend_compiler_globals, Compiler_globals)
- #endif
- ZEND_INI_END()
-
-
- コードをコピー
-
-
zend.enable_gc 対応する操作関数は ZEND_INI_MH (OnUpdateGCEnabled) です。ガベージ コレクション メカニズムがオンになっている場合、つまり GC_G (gc_enabled) が true の場合、gc_init 関数が呼び出され、ガベージ コレクション メカニズムの初期化操作が実行されます。 gc_init 関数は zend/zend_gc.c の 121 行目にあります。この関数はガベージ コレクション メカニズムがオンになっているかどうかを決定します。オンになっている場合、メカニズム全体が初期化されます。つまり、割り当てのために malloc が直接呼び出されます。キャッシュ リスト全体に対して 10,000 個の gc_root_buffer メモリ スペース。 ここでの 10000 はコード内にハードコーディングされており、マクロ GC_ROOT_BUFFER_MAX_ENTRIES として存在します。この値を変更する必要がある場合は、ソース コードを変更して PHP を再コンパイルする必要があります。 gc_init 関数は、メモリの事前割り当て後に gc_reset 関数を呼び出し、メカニズム全体で使用されるいくつかのグローバル変数をリセットします。たとえば、gc 実行数 (gc_runs) や gc 内のガベージ (収集された) の数の統計を 0 に設定します。 、および二重リンクリストの先頭ノードを設定します。前のノードと次のノードはそれ自体を指します。ガベージ コレクション メカニズム用に前述したグローバル変数に加えて、他にも一般的に使用される変数があり、その一部については以下で説明します。
typedef struct _zend_gc_globals {- zend_bool gc_enabled; /* ガベージコレクションメカニズムを有効にするかどうか*/
- zend_bool gc_active; /* 事前に割り当てられたバッファー配列、デフォルトは 10000 (バッファーの事前割り当て配列) */
- gc_root_buffer root; /* サイクルの可能なルートのリスト */
- 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_uintcollected; /* gc 内のガベージの数 */
- // 省略...
- }
-
-
- コードをコピー
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 _zval_ptr_dtor(zval **zval_ptr ZEND_FILE_LINE_DC) /* {{{ */ {- #if DEBUG_ZEND>=2
- printf("%x (%x) の参照カウントを削減しています: %d-> ;%DN ", *ZVAL_PTR, ZVAL_PTR, Z_REFCOUNT_PP (ZVAL_PTR), Z_REFCOUNT_PP (ZVAL_PTR) - 1); Fcount_pp (zval_ptr) == 0) {
- TSRMLS_FETCH ();
- if (*zval_ptr != &EG(uninitialized_zval)) {
- GC_REMOVE_ZVAL_FROM_BUFFER(*zval_ptr);
- zval_dtor(*zval_ptr);
- efree_rel(*zval_ptr);
- }
- } else {
- TSRMLS_FETCH();
- if (Z_RE FCOUNT_PP(zval_ptr ) == 1) {
- Z_ UNSET_ISREF_PP(zval_ptr) ;
- }
- GC_ZVAL_CHECK_POSSIBLE_ROOT(*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 &&
- GC_ZVAL_ADDRESS(zv) != NULL &&
- GC_ZVAL_GET_COL OR(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);
- return;
- }
- GC_BENCH_INC(zval_possible_root);
- if (GC_ZVAL_GET_COLOR(zv) != GC_ RPLE) {
- GC_ZVAL_SET_PURPLE (zv);
- if (!GC_ZVAL_ADDRESS(zv)) {
- gc_root_buffer *newRoot = GC_G(未使用);
- if (newRoot) {
- GC_G(未使用) = 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-&gt; refcount__gc ++;
- gc_collect_cycles(tsrmls_c);
- GC _G (未使用) = 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->ハンドル = 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);
- }
- }
- }
-
-
- コードをコピー
-
-
前述したように、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 灰色は、refcount を 1 つ減らす操作が実行されたことを示します
GC_BLACK 黒はデフォルトの色で、通常の色です。
関連するタグとオペレーションコードは次のとおりです。
-
- #define GC_COLOR 0x03
- #define GC_BLACK 0x00
- #define GC_WHITE 0x01
- #define GC_GREY 0x02
- #define GC_PURPLE 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 ソース コードでより頻繁に使用されます。メモリ管理で使用されます。 、など。これは比較的効率的で経済的な解決策です。ただし、データベースを設計するときは、このメソッドをフィールドに使用できない場合があります。より直感的で読みやすい方法で実装する必要があります。
|