Maison  >  Article  >  développement back-end  >  Une brève discussion du code source PHP 34 : le nouveau mécanisme de récupération de place de PHP5.3 (Garbage Collection)

Une brève discussion du code source PHP 34 : le nouveau mécanisme de récupération de place de PHP5.3 (Garbage Collection)

不言
不言original
2018-06-29 10:07:011030parcourir

Cet article présente principalement le code source PHP trente-quatre : le mécanisme de récupération de place nouvellement ajouté (Garbage Collection) de PHP5.3, qui a une certaine valeur de référence. Maintenant, je le partage avec vous. Les amis dans le besoin peuvent s'y référer. .

Une brève discussion du code source PHP 34 : le nouveau mécanisme de récupération de place de PHP5.3 (Garbage Collection)
Dans l'article précédent, une brève discussion du code source PHP 33 : PHP5.3. nouveaux ajouts Les bases du garbage collection présentent quelques connaissances de base du mécanisme de garbage collection. Aujourd'hui, nous examinons son initialisation, en ajoutant au processus de tampon de mémoire et de collecte des ordures.
Pour la documentation officielle, veuillez cliquer sur Garbage Collection
Adresse de la version chinoise : http://docs.php.net/manual/zh/features.gc.php
[Initialisation]
Dans zend/ zend_gc .c La ligne 121 a la fonction gc_init qui implémente l'initialisation gc. Le code est le suivant :

 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);
}}

La ligne 123 détermine si elle est vide et si gc est activé. Si les deux sont vrais, passez à la ligne. 124
La ligne 124 appelle directement malloc pour allouer 10 000 mémoire gc_root_buffer.
La ligne 125 définit la variable globale last_unused à la position finale du tampon gc.
La ligne 126 réinitialise l'ensemble du mécanisme de récupération de place. Le code commence à partir de la ligne 88 de zend/zend_gc.c, comme suit :

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;
}}

La ligne 90~91 définit les statistiques du nombre d'exécutions de gc. (gc_runs) Et le nombre de déchets (collectés) dans gc est 0.
Les lignes 106 ~ 107 définissent le nœud précédent et le nœud suivant du nœud principal de la liste doublement chaînée pour qu'ils pointent vers lui-même.

Concernant gc_enabled, il est activé par défaut et peut être configuré dans php.ini.
Le code d'implémentation est le suivant à la ligne 93 de zend/zend.c :

STD_ZEND_INI_BOOLEAN("zend.enable_gc","1",ZEND_INI_ALL,OnUpdateGCEnabled,   gc_enabled, zend_gc_globals,        gc_globals)

L'appel d'initialisation est à la ligne 79 de zend/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;}

[Ajouter au tampon poubelle ]

Suivant le code source de PHP ZEND/ZEND_EXECUTE_API.C 424 ligne
[_ zval_ptr_dtor] -& gt; [gc_zval_possible_root ()]
Dans la fonction gc_zval_check_possible_root(), seules les opérations de garbage collection sont effectuées sur les tableaux et les objets

Le code de la fonction gc_zval_possible_root est le suivant :

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);
}
}}
Les lignes 132 à 140 vérifient si les informations du nœud zval ont été placées dans le tampon du nœud. Si elles ont été placées dans le tampon du nœud, retournez-les directement.

Les lignes 142 à 145 traitent l'objet. nœud et revenez directement sans effectuer d'opérations ultérieures.

La ligne 149 détermine si le nœud a été marqué en violet. S'il est violet, il ne sera plus ajouté au tampon du nœud. ajouté seulement une fois au tampon.

La ligne 150 marque la couleur du nœud en violet, indiquant que le nœud a été ajouté au tampon et qu'il n'est pas nécessaire de l'ajouter la prochaine fois

Les lignes 153 à 157 trouvent le nouveau nœud Si le tampon est plein, une opération de garbage collection est effectuée.

Les lignes 176 à 184 ajoutent de nouveaux nœuds à la liste doublement chaînée où se trouve le tampon.

[Processus de garbage collection]

Dans la fonction gc_zval_possible_root, lorsque le tampon est plein, le programme appelle la fonction gc_collect_cycles pour effectuer des opérations de garbage collection. À partir de la ligne 615 du fichier zend/zend_gc.c, le code d'implémentation est le suivant :

 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;}
La ligne 619 détermine si le tampon est vide. S'il est vide, le garbage collection ne sera pas effectué. 🎜> Ligne 622 Ligne 1 détermine si l'opération de garbage collection est en cours. Si elle est en cours, retournez directement

Lignes 625 à 627 pour augmenter le nombre d'opérations de garbage collection de 1, initialiser la liste libre et définir gc_active. à 1 pour indiquer que le garbage collection est en cours
Ligne 628 Voici l'étape B de l'algorithme dans son document officiel. L'algorithme utilise une recherche en profondeur d'abord pour trouver toutes les racines possibles. Après l'avoir trouvé, le nombre de références dans chaque variable. conteneur est décrémenté de 1″ pour garantir que la même variable conteneur n'est pas décrémentée deux fois. "1", marqué en gris qui a été réduit de 1.
Ligne 629 C'est l'étape C de l'algorithme que l'algorithme utilise à nouveau. une recherche en profondeur d'abord pour chaque nœud racine et vérifie le nombre de références de chaque conteneur de variable. Le nombre est de 0 et les conteneurs de variables sont marqués en blanc. Si le nombre de références est supérieur à 0, l'opération consiste à décrémenter le nombre de références de 1. l'utilisation de la recherche en profondeur à ce stade est reprise (c'est-à-dire que le nombre de références est augmenté de 1), puis ils sont à nouveau marqués en noir.
La dernière étape D de l'algorithme à la ligne 630, l'algorithme parcourt le. tampon racine pour supprimer les racines du conteneur de variables (racines zval) et en même temps, vérifie s'il existe des conteneurs de variables marqués en blanc à l'étape précédente. Les conteneurs de variables marqués sont effacés
Dans [gc_collect_cycles. () -> gc_collect_roots() -> zval_collect_white() ] nous pouvons voir que les nœuds marqués en blanc seront ajoutés à la variable globale zval_to_free list . stockez les variables globales free_list et next_to_free dans les variables temporaires correspondantes, et seront restaurées à leur état actuel à la fin
Lignes 634~635 initialisent la liste qui doit être effacée, effacent la liste zval à effacer, et définissez l'état de l'opération de récupération de place sur un état inactif.
Les lignes 639 à 655 appellent le destructeur pour la première fois et comptent le nombre de variables effacées
Lignes 657 à 678, variables claires
Lignes 682 à 686. libérer la mémoire
Les lignes 687 à 689 traitent les statistiques du nombre de déchets et restaurent les variables free_list et next_to_free

Ce qui précède est l'intégralité du contenu de cet article, j'espère qu'il sera utile à l'apprentissage de chacun. Pour plus de contenu connexe, veuillez faire attention au site Web PHP chinois

Recommandations associées :

Une brève discussion du code source PHP 33 : bases du nouveau mécanisme de récupération de place (Garbage Collection) ajouté dans PHP5.3

Une brève discussion de Code source PHP 32 : couche emalloc/efree et couche de tas dans le pool de mémoire PHP

Une brève discussion du code source PHP vingt-neuf : à propos de l'héritage des interfaces

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn