ホームページ  >  記事  >  バックエンド開発  >  PHP5.3のガベージコレクション機構について

PHP5.3のガベージコレクション機構について

WBOY
WBOYオリジナル
2016-07-25 09:10:161025ブラウズ
  1. struct _zval_struct {
  2. /* 変数情報 */
  3. zvalue_value value; /* value */
  4. zend_uint refcount__gc;
  5. zend_uchar type; /* アクティブな型 */
  6. zend_uchar is_ref__gc;
  7. };
コードをコピー
PHP5.3より前のバージョンと比較すると、新しいガベージコレクション機構のために、参照カウントフィールドrefcountと参照フィールドis_refの後に__gcが追加されています。 PHP のソース コード スタイルでは、多数のマクロが非常に特徴的です。これらのマクロは、インターフェイス層に相当し、ALLOC_ZVAL マクロなど、インターフェイス層の下にある一部の実装を保護します。このマクロは、PHP のメモリ管理割り当て関数 emalloc を直接呼び出してメモリを割り当てていました。 by 変数の型とサイズが決定されます。 ガベージ コレクション メカニズムの導入後、ALLOC_ZVAL マクロは新しいガベージ コレクション ユニット構造を直接採用します。割り当てられるサイズはすべて同じであり、メモリを割り当てた後、zval_gc_info 構造によって占有されるメモリ サイズになります。この構造体は初期化されます。

  1. /* 次のマクロは、zend_alloc.h のマクロをオーバーライドします */
  2. #undef ALLOC_ZVAL
  3. #define ALLOC_ZVAL(z)
  4. do {
  5. (z) = (zval*)emalloc(sizeof(zval_gc_info));
  6. GC_ZVAL_INIT(z);
  7. } 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 {
  1. zval z;
  2. union {
  3. gc_root_buffer *buffered;
  4. struct _zval_gc_info *next;
  5. } u;
  6. } コピーコード
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);
  1. }
  2. return SUCCESS;
  3. }
  4. /* }}} */
  5. ZEND_INI_BEGIN()
  6. ZEND_INI_ENTRY("error_reporting", NULL, ZEND_INI_ALL, OnUpdateErrorReporting)
  7. STD_ZEND_INI_BOOLEAN("zend.enable_gc", "1", ZEND_INI_ALL, OnUpdateGCEnabled, gc_enabled, zend_gc_globals, gc_globals)
  8. #ifdef ZEND_MULTIBYTE
  9. STD_ZEND_INI_BOOLEAN("detect_unicode", "1", ZEND_INI_ALL, OnUpdateBool, detect_unicode, zend_compiler_globals, Compiler_globals)
  10. #endif
  11. ZEND_INI_END()
  12. コードをコピー
  13. 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 {
    1. zend_bool gc_enabled; /* ガベージコレクションメカニズムを有効にするかどうか*/
    2. zend_bool gc_active; /* 事前に割り当てられたバッファー配列、デフォルトは 10000 (バッファーの事前割り当て配列) */
    3. gc_root_buffer root; /* サイクルの可能なルートのリスト */
    4. gc_root_buffer *unused; /* 未使用のバッファーのリスト */
    5. gc_root_buffer *first_unused; /* 最初へのポインター未使用のバッファ ノード (最初の未使用のバッファへのポインタ) */
    6. gc_root_buffer *last_unused; /* 最後の未使用のバッファ ノードへのポインタ、この最後の未使用のバッファへのポインタ */
    7. zval_gc_info *zval_to_free; /* 解放する zval の一時リスト */
    8. zval_gc_info *free_list; /* 一時変数、必須 解放されたリストの先頭 */
    9. zval_gc_info *next_to_free; /* 一時変数、次に解放される変数の場所 */
    10. zend_uint gc_runs; /* gc 実行数の統計*/
    11. zend_uintcollected; /* gc 内のガベージの数 */
    12. // 省略...
    13. }
    14. コードをコピー
    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) /* {{{ */
      {
    1. #if DEBUG_ZEND>=2
    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) {
    3. TSRMLS_FETCH ();
    4. if (*zval_ptr != &EG(uninitialized_zval)) {
    5. GC_REMOVE_ZVAL_FROM_BUFFER(*zval_ptr);
    6. zval_dtor(*zval_ptr);
    7. efree_rel(*zval_ptr);
    8. }
    9. } else {
    10. TSRMLS_FETCH();
    11. if (Z_RE FCOUNT_PP(zval_ptr ) == 1) {
    12. Z_ UNSET_ISREF_PP(zval_ptr) ;
    13. }
    14. GC_ZVAL_CHECK_POSSIBLE_ROOT(*zval_ptr);
    15. }
    16. }
    17. /* }}} */
    18. コードをコピー
    19. コードから、この zval の破棄プロセスが明確にわかります。次の 2 つの操作が参照カウント フィールドに対して実行されます。 変数の参照カウントが 1 の場合、つまり参照カウントが 1 減算されて 0 になった場合、変数は直接クリアされます。現在の変数がキャッシュされている場合、変数の参照カウントが 1 より大きい場合、つまり、1 を引いた後の参照カウントが 0 より大きい場合、変数はガベージ リストに入れられます。変更に参照がある場合は、その参照を削除します。

      変数をガベージ リストに入れる操作は GC_ZVAL_CHECK_POSSIBLE_ROOT で、これもマクロで関数 gc_zval_check_possible_root に対応しますが、この関数は配列とオブジェクトに対してガベージ コレクション操作のみを実行します。配列変数とオブジェクト変数の場合は、gc_zval_possible_root 関数を呼び出します。

      1. ZEND_API void gc_zval_possible_root(zval *zv TSRMLS_DC)
      2. {
      3. if (UNEXPECTED(GC_G(free_list) != NULL &&
      4. GC_ZVAL_ADDRESS(zv) != NULL &&
      5. GC_ZVAL_GET_COL OR(zv) == GC_BLACK) &&
      6. (GC_ZVAL_ADDRESS(zv) < GC_G(buf) ||
      7. GC_ZVAL_ADDRESS(zv) >= GC_G(last_unused))) {
      8. /* 指定された zval は、
      9. * 現在実行中の GC によって削除される予定のガベージです* /
      10. return;
      11. }
      12. if (zv->type == IS_OBJECT) {
      13. GC_ZOBJ_CHECK_POSSIBLE_ROOT(zv);
      14. return;
      15. }
      16. GC_BENCH_INC(zval_possible_root);
      17. if (GC_ZVAL_GET_COLOR(zv) != GC_ RPLE) {
      18. GC_ZVAL_SET_PURPLE (zv);
      19. if (!GC_ZVAL_ADDRESS(zv)) {
      20. gc_root_buffer *newRoot = GC_G(未使用);
      21. if (newRoot) {
      22. GC_G(未使用) = newRoot->prev;
      23. } else if (GC_G (first_unused ) != GC_G(last_unused)) {
      24. newRoot = GC_G(first_unused);
      25. GC_G(first_unused)++;
      26. } else {
      27. if (!GC_G(gc_enabled)) {
      28. GC_ZVAL_SET_BLACK(zv);
      29. return;
      30. }
      31. Zv-&gt; refcount__gc ++;
      32. gc_collect_cycles(tsrmls_c);
      33. GC _G (未使用) = newRoot->prev;
      34. }
      35. newRoot->next = GC_G(roots).next;
      36. newRoot->prev = &GC_G(roots);
      37. GC_G(roots).next-> ;prev = newRoot;
      38. GC_G(roots).next = newRoot;
      39. GC_ZVAL_SET_ADDRESS(zv, newRoot);
      40. newRoot->ハンドル = 0;
      41. newRoot->u.pz = zv;
      42. GC_BENCH_INC(zval_buffered);
      43. GC_BENCH_INC(root_buf _length );
      44. GC_BENCH_PEAK(root_buf_peak, root_buf_length);
      45. }
      46. }
      47. }
      48. コードをコピー
      49. 前述したように、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 黒はデフォルトの色で、通常の色です。 関連するタグとオペレーションコードは次のとおりです。

        1. #define GC_COLOR 0x03
        2. #define GC_BLACK 0x00
        3. #define GC_WHITE 0x01
        4. #define GC_GREY 0x02
        5. #define GC_PURPLE 0x03
        6. #define GC_ADDRESS(v )
        7. ((gc_root_buffer*)((zend_uintptr_t)(v ) ) & ~GC_COLOR))
        8. #define GC_SET_ADDRESS(v, a)
        9. (v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & GC_COLOR) | ((zend_uintptr_t)(a)))
        10. #define GC_GET_COLOR(v)
        11. (((zend_uintptr_t)(v)) & GC_COLOR)
        12. #define GC_SET_COLOR(v, c)
        13. (v) = ((gc_root_buffer*)((((zend_uintptr_t)(v)) & ~ GC_COLOR) | (c)))
        14. #define GC_SET_BLACK(v)
        15. (v) = ((gc_root_buffer*)((zend_uintptr_t)(v)) & ~GC_COLOR))
        16. #define GC_SET_PURPLE(v)
        17. (v ) = ((gc_root_buffer*)((zend_uintptr_t)(v)) | GC_PURPLE))
        コードをコピー

        上記のビットでステータスをマークする方法は、PHP ソース コードでより頻繁に使用されます。メモリ管理で使用されます。 、など。これは比較的効率的で経済的な解決策です。ただし、データベースを設計するときは、このメソッドをフィールドに使用できない場合があります。より直感的で読みやすい方法で実装する必要があります。



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