ホームページ > バックエンド開発 > PHPチュートリアル > PHP5.3のガベージコレクションメカニズム(動的ストレージ割り当てスキーム)を深く理解する_PHPチュートリアル

PHP5.3のガベージコレクションメカニズム(動的ストレージ割り当てスキーム)を深く理解する_PHPチュートリアル

WBOY
リリース: 2016-07-21 15:14:42
オリジナル
902 人が閲覧しました

ガベージ コレクション メカニズムは、動的なストレージ割り当てスキームです。プログラムで不要になった割り当てられたメモリ ブロックを自動的に解放します。 メモリを自動的に再利用するプロセスは、ガベージ コレクションと呼ばれます。ガベージ コレクション メカニズムにより、プログラマはプログラム メモリの割り当てについてあまり心配する必要がなく、ビジネス ロジックにより多くのエネルギーを注ぐことができます。 現在人気のあるさまざまな言語の中でも、ガベージ コレクション メカニズムは新世代の言語に共通の機能です。たとえば、Python、PHP、Eiffel、C#、Ruby などはすべてガベージ コレクション メカニズムを使用しています。 ガベージ コレクションは現在一般的な行為ですが、それはもはや若いものではありません。 1960 年代には MIT によって開発された Lisp システムに存在していましたが、当時の技術的条件が未熟だったため、1990 年代に Java が登場するまで、ガベージ コレクション メカニズムは一見美しいテクノロジになりました。収集メカニズムは広く使用されています。

PHP は、言語層でのメモリの動的管理も実装しています。これについては、前の章で詳しく説明しましたが、メモリの動的管理により、開発者は煩雑なメモリ管理から解放されます。これに加えて、PHP は言語層でガベージ コレクション メカニズムも提供するため、プログラマはプログラムのメモリ割り当てについてあまり心配する必要がなくなります。

PHP5.3 より前の PHP には、参照カウントに基づく単純なガベージ コレクションしかありませんでした。変数の参照カウントが 0 になると、PHP はメモリ内の変数を破棄しますが、ここでのガベージはガベージとは言えません。 そして、PHP は、ライフサイクルの終了後にこのプロセス/スレッドによってクリックされたコンテンツを解放します。この方法により、PHP は初期段階であまり多くのメモリ リークを考慮する必要がないと判断されます。 しかし、PHP の発展、PHP 開発者の増加、および PHP が扱うビジネス範囲の拡大に伴い、PHP5.3 ではより完全なガベージ コレクション メカニズムが導入されました。 新しいガベージ コレクション メカニズムは、サイクルを処理できない参照メモリ リークの問題を解決します。 PHP5.3 のガベージ コレクション メカニズムは、記事の「参照カウント システムにおける同時サイクル コレクション」の同期アルゴリズムを使用します。このアルゴリズムの導入については詳しく説明しません。PHP の公式ドキュメント「Collecting Cycles」に図解による導入があります。
前述したように、PHP では主なメモリ管理方法は参照カウントです。ガベージ コレクション機構を導入する目的は、参照カウントにおける循環参照を解消し、これによるメモリ リークを防ぐことです。 ガベージ コレクション メカニズムは、PHP の動的メモリ管理に基づいて存在します。ガベージ コレクション メカニズムを導入するために、PHP5.3 では、以下に示すように、変数ストレージの基本構造がいくつか変更されています:

コードをコピー コードは次のとおりです:

struct _zval_struct {
/*変数情報 */
zvalue_value value; /* value */
zend_uchar type */


PHP5.3 より前のバージョンとの比較そして、フィールド 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); 一方、(0)


zend_gc.h ファイルは zend.h の 749 行目で参照されます。 h" によりカバレッジが置き換えられます。237 行目で参照されている zend_alloc.h ファイル内の ALLOC_ZVAL などのマクロが置き換えられました。新しいマクロでの主な変更点は、割り当てられたメモリ サイズと割り当て内容の変更です。ガベージ コレクション メカニズムが追加されました。前の純粋なメモリ割り当ての内容に追加されると、すべての内容が zval_gc_info 構造体に含まれます。


コードをコピーする
コードは次のとおりです。バッファ済み; struct _zval _gc_info *次 } zval_gc_info;
ZVAL コンテナに格納されている変数には、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, ステージ 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_Z END_INI_B OOLEAN("detect_unicode", "1", ZEND_INI_ALL、OnUpdateBool、detect_unicode、zend_compiler_globals、compiler_globals)
#endif
ZEND_INI_END()

zend.enable_gc ガベージ コレクション メカニズムがオンになっている場合、つまり GC_G (gc_enabled) の対応する操作関数は ZEND_INI_MH (OnUpdateGCEnabled) です。 )は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; /* 進行中かどうか*/
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 ソース コードでより頻繁に使用されます。これは、より効率的で経済的なソリューションです。ただし、データベースを設計するときは、このメソッドをフィールドに使用できない場合があります。より直感的で読みやすい方法で実装する必要があります。

www.bkjia.comtru​​ehttp://www.bkjia.com/PHPjc/326234.html技術記事ガベージ コレクション メカニズムは、動的なストレージ割り当てスキームです。プログラムで不要になった割り当てられたメモリ ブロックを自動的に解放します。 メモリを自動的に再利用するプロセスは、ガベージ コレクションと呼ばれます。ガベージ コレクション メカニズムは...
ソース:php.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート