Heim  >  Artikel  >  Backend-Entwicklung  >  Eine kurze Diskussion des PHP-Quellcodes 34: Der neu hinzugefügte Garbage-Collection-Mechanismus von PHP5.3 (Garbage Collection)

Eine kurze Diskussion des PHP-Quellcodes 34: Der neu hinzugefügte Garbage-Collection-Mechanismus von PHP5.3 (Garbage Collection)

不言
不言Original
2018-06-29 10:07:011028Durchsuche

Dieser Artikel stellt hauptsächlich den PHP-Quellcode 34 vor: den neu hinzugefügten Garbage Collection-Mechanismus (Garbage Collection), der einen gewissen Referenzwert hat. Jetzt kann ich ihn mit Ihnen teilen .

Eine kurze Diskussion des PHP-Quellcodes 34: Der neue Garbage-Collection-Mechanismus (Garbage Collection)
Im vorherigen Artikel eine kurze Diskussion des PHP-Quellcodes 33: PHP5.3 Neue Ergänzungen Die Grundlagen der Garbage Collection führen in einige Grundkenntnisse des Garbage Collection-Mechanismus ein. Heute schauen wir uns die Initialisierung an und erweitern den Garbage-Puffer- und Garbage-Collection-Prozess.
Für die offizielle Dokumentation klicken Sie bitte auf Garbage Collection
Adresse der chinesischen Version: http://docs.php.net/manual/zh/features.gc.php
[Initialisierung]
In zend/ zend_gc .c Zeile 121 hat die Funktion gc_init, die die Initialisierung von gc implementiert. Der Code lautet wie folgt:

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

Zeile 123 bestimmt, ob es leer ist und ob gc aktiviert ist. Wenn beide wahr sind, gehe zu Zeile 124
Zeile 124 ruft malloc direkt auf, um 10.000 gc_root_buffer-Speicher zuzuweisen.
Zeile 125 setzt die globale Variable last_unused auf das Ende des GC-Puffers.
Zeile 126 setzt den gesamten Garbage-Collection-Mechanismus zurück. Der Code beginnt in Zeile 88 von zend/zend_gc.c wie folgt:

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

Die Zeilen 90–91 legen die Statistik der Anzahl der GC-Läufe fest (gc_runs ) und gc Die Anzahl des Mülls (gesammelt) beträgt 0.
Die Zeilen 106–107 legen den vorherigen Knoten und den nächsten Knoten des Kopfknotens der doppelt verknüpften Liste so fest, dass sie auf sich selbst zeigen.

Was gc_enabled betrifft, ist es standardmäßig aktiviert und kann in php.ini konfiguriert werden.
Der Implementierungscode lautet wie folgt in Zeile 93 von zend/zend.c:

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

Der Initialisierungsaufruf befindet sich in Zeile 79 von 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;}

[Zum Müll hinzufügen buffer]
Verfolgen Sie den PHP-Quellcode zend/zend_execute_API.c Zeile 424
[_zval_ptr_dtor] -> [GC_ZVAL_CHECK_POSSIBLE_ROOT()] -> [gc_zval_possible_root()]
wo Führen Sie in der Funktion gc_zval_check _possible_root() nur Garbage Collection-Vorgänge für Arrays und Objekte aus

Der Code der Funktion gc_zval_possible_root lautet wie folgt:

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

Zeilen 132–140 prüfen, ob die zval-Knoteninformationen vorhanden sind wurde in den Knotenpuffer gestellt. Wenn es in den Knotenpuffer gestellt wurde, kehre direkt zurück, was seine Leistung optimieren kann

Die Zeilen 142 bis 145 verarbeiten den Objektknoten und kehren direkt zurück, ohne nachfolgende Vorgänge auszuführen

Zeile 149 bestimmt, ob der Knoten lila markiert wurde. Wenn er lila ist, wird er nicht mehr zum Knotenpuffer hinzugefügt. Dadurch wird sichergestellt, dass ein Knoten nur einmal zum Puffer hinzugefügt wird.

Zeile 150 markiert die Farbe des Knotens als lila, was darauf hinweist, dass der Knoten zum Puffer hinzugefügt wurde und beim nächsten Mal nicht hinzugefügt werden muss.

Zeilen 153 bis 157 finden den neuer Knoten Wenn der Puffer voll ist, wird ein Garbage-Collection-Vorgang durchgeführt.

Die Zeilen 176–184 fügen neue Knoten zur doppelt verknüpften Liste hinzu, in der sich der Puffer befindet.

[Garbage-Collect-Prozess]
Wenn in der Funktion gc_zval_possible_root der Puffer voll ist, ruft das Programm die Funktion gc_collect_cycles auf, um Garbage-Collection-Vorgänge durchzuführen. Ab Zeile 615 der Datei zend/zend_gc.c lautet der Implementierungscode wie folgt:

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

Zeile 619 bestimmt, ob der Puffer leer ist, wird keine Speicherbereinigung durchgeführt
Zeile 622 bestimmt, ob der Garbage-Collection-Vorgang regelmäßig ausgeführt wird. Wenn er gerade ausgeführt wird, kehren Sie direkt zurück
Zeilen 625 bis 627, um die Anzahl der Garbage-Collection-Vorgänge um 1 zu erhöhen, die freie Liste zu initialisieren und gc_active auf 1 zu setzen Geben Sie an, dass die Müllrückgabe im Gange ist
Zeile 628 hier Als Schritt B des Algorithmus in seinem offiziellen Dokument verwendet der Algorithmus die Tiefensuche, um alle möglichen Wurzeln zu finden. Nachdem er sie gefunden hat, wird der Referenzzähler in jedem Variablencontainer dekrementiert um 1", um sicherzustellen, dass derselbe Variablencontainer nicht zweimal dekrementiert wird. ″, grau markiert, das um 1 reduziert wurde.
Zeile 629 Dies ist Schritt C des Algorithmus. Der Algorithmus verwendet erneut eine Tiefensuche nach Jeder Wurzelknoten und überprüft den Referenzzähler jedes Variablencontainers. Wenn der Referenzzähler 0 ist, wird der Variablencontainer weiß markiert. Wenn die Anzahl der Referenzen größer als 0 ist, wird der Referenzzähler mithilfe der Tiefe um 1 verringert. An diesem Punkt wird die erste Suche fortgesetzt (dh der Referenzzähler wird um 1 erhöht) und dann erneut mit Schwarz markiert 🎜>Im letzten Schritt D des Algorithmus in Zeile 630 durchläuft der Algorithmus den Root-Puffer, um ihn zu entfernen von dort aus die Variablencontainer-Wurzeln (zval-Wurzeln) und prüft gleichzeitig, ob Variablencontainer vorhanden sind, die im vorherigen Schritt weiß markiert wurden. Die Variablencontainer werden gelöscht ;gc_collect_roots() -> zval_collect_white() ] Wir können sehen, dass die weiß markierten Knoten zur Liste der globalen Variablen zval_to_free hinzugefügt werden. Die Zeilen 632–633 speichern die globalen Variablen free_list und next_to_free in den entsprechenden temporären Variablen und werden am Ende in ihrem aktuellen Zustand wiederhergestellt. Die Zeilen 634 bis 635 werden initialisiert, die zu löschende Zval-Liste und der Garbage Collection-Vorgang Status ist inaktiv.
Zeilen 639–655 rufen den Destruktor zum ersten Mal auf und zählen die Anzahl der gelöschten Variablen
Zeilen 639–678 löschen Variablen
Zeilen 682–686 geben Speicher frei
Die Zeilen 687 bis 689 verarbeiten die Garbage-Count-Statistiken und stellen die Variablen free_list und next_to_free wieder her.

Das Obige ist der gesamte Inhalt dieses Artikels, der für Sie hilfreich sein wird Bitte beachten Sie die chinesische PHP-Website

Verwandte Empfehlungen:

Eine kurze Diskussion des PHP-Quellcodes 33: Die Grundlagen des neuen Garbage Collection-Mechanismus (Garbage Collection) in PHP5.3

Eine kurze Beschreibung Diskussion des PHP-Quellcodes 32: Emalloc/Efree-Schicht und Heap-Schicht im PHP-Speicherpool

Eine kurze Diskussion des PHP-Quellcodes 29: Über die Vererbung von Schnittstellen

Das obige ist der detaillierte Inhalt vonEine kurze Diskussion des PHP-Quellcodes 34: Der neu hinzugefügte Garbage-Collection-Mechanismus von PHP5.3 (Garbage Collection). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn