Detaillierte grafische Code-Erklärung des PHP-Kernel-Speichermechanismus (Trennung/Änderung)

黄舟
Freigeben: 2023-03-06 13:06:02
Original
1331 Leute haben es durchsucht

Vorwort:

Die meisten Programmierer, die Blogs lesen, lesen möglicherweise nicht gerne Artikel mit vielen chinesischen Schriftzeichen, aber dieser Artikel führt Beschreibungen ein, die auf Chinesisch basieren Charaktere, wenn Sie es geduldig lesen, wird es für die meisten Menschen definitiv lohnend sein!

Vielleicht wissen Sie es, vielleicht wissen Sie es auch nicht, PHP ist eine schwach typisierte, dynamische Skriptsprache. Der sogenannte schwache Typ bedeutet, dass PHP den Variablentyp nicht streng überprüft (streng genommen ist PHP eine mittelstark typisierte Sprache, bei der Deklaration einer Variablen muss der Typ der gespeicherten Daten nicht explizit angegeben werden). Zum Beispiel: $a = 1; (Ganzzahl) $a="1"; (String)

Ich habe immer PHP verwendet, aber was genau ist das und wie wird die zugrunde liegende Implementierung erreicht, um PHP zu einer so praktischen und schnellen schwach typisierten Sprache zu machen?

Ich habe in letzter Zeit viele Bücher und verwandte Blog-Informationen gelesen und viel über die Mechanismen von PHP gelernt Kernel.
Ein einfaches Verständnis von PHP ist eine C-Sprachklassenbibliothek. Wenn Sie zu php.net gehen und den Quellcode herunterladen, finden Sie zunächst den Kern PHP ist die Zend-Engine. Dabei handelt es sich um eine in C-Sprache geschriebene Funktionsbibliothek, die zur Verwaltung der zugrunde liegenden Funktionsverwaltung, Speicherverwaltung, Klassenverwaltung und Variablenverwaltung verwendet wird. Sie haben viele Erweiterungen für den Kernel geschrieben, von denen die meisten unabhängig sind. Um eine Betriebssystem-Metapher zu verwenden: zend Engine ist ein Betriebssystem und stellt offiziell viele „Anwendungen“ bereit, aber diese „Anwendung“ ist kein Media Play, sondern MySQL, Libxml und Dom. Natürlich können Sie auch eigene Erweiterungen auf Basis der API der Zend Engine entwickeln.


Beginnen wir mit der Einführung des Speichermechanismus von PHP-Variablen im Kernel.

PHP ist eine typisierte Sprache, was bedeutet, dass eine PHP-Variable jeden Datentyp speichern kann. Aber PHP ist in der C-Sprache geschrieben, und die C-Sprache ist eine stark typisierte Sprache, oder? Jede Variable hat einen festen Typ (eine wird durch einen starken Typ konvertiert, es kann jedoch zu Problemen kommen). ? um einen beliebigen Datentyp in einer Variablen zu speichern? Bitte sehen Sie sich die Speicherstruktur unten an.

Öffnen Sie die Header-Datei Zend/zend.h und Sie finden die folgende Struktur Zval:


1.zval-Struktur

 typedef struct _zval_struct zval;
Nach dem Login kopieren
 typedef union _zvalue_value {
    long lval;      /* long value */
    double dval;    /* double value */
    struct {
    char *val; //4字节
    int len;   //4字节
    } str;
    HashTable *ht;    /* hash table value */
    zend_object_value obj;
 } zvalue_value;
Nach dem Login kopieren


 struct _zval_struct {
    /* Variable information */
    zvalue_value value;  /* 变量值保存在这里 12字节*/
    zend_uint refcount;//4字节,变量引用计数器
    zend_uchar type;   /* active type变量类型 1字节*/
    zend_uchar is_ref;//是否变量被&引用,0表示非引用,1表示引用,1字节
    };
Nach dem Login kopieren


2.zend_uchar Typ

Variablen in PHP umfassen vier Skalartypen (bool, int, float, string), zwei A-Komposite Typ (Array, Objekt) und zwei spezielle Typen (Ressource und NULL). Innerhalb von Zend entsprechen diese Typen den folgenden Makros (Codespeicherort phpsrc/Zend/zend.h) Zend bestimmt anhand des Typwerts, auf welches Element von value zugegriffen werden soll. Die verfügbaren Werte sind wie folgt:


3
.zend_uint refcount__gc

Dieser Wert ist eigentlich ein Zähler, um zu speichern, wie viele Variablen (oder Symbole, Symbole, alle Symbole in der Symboltabelle vorhanden sind (symble Tabelle), verschiedene Bereiche verwenden unterschiedliche Symboltabellen, wir werden dies später besprechen) zeigt auf zval. Wenn eine Variable generiert wird, ist ihr Refcount = 1. Typische Zuweisungsoperationen wie $a = $b erhöhen den Refcount von zval um 1, und die nicht gesetzte Operation verringert ihn entsprechend um 1. Vor PHP5.3 wurde der Referenzzählmechanismus zur Implementierung von GC verwendet. Wenn der Refcount eines zval kleiner als 0 war, ging die Zend-Engine davon aus, dass keine Variable auf den zval verweist, und gab daher den von ihm belegten Speicherplatz frei der Zval. Aber manchmal sind die Dinge nicht so einfach. Wir werden später sehen, dass Der einfache Referenzzählmechanismus den Zval von Zirkelverweisen nicht GC ermitteln kann (Einzelheiten siehe Beispiel 3 unten), selbst wenn die Variable zeigt zum zval wurde deaktiviert, was zu einem Speicherverlust führte (Memory Leck).

4.is_ref__gc
.

Dieses Feld wird verwendet, um zu markieren, ob die Variable eine Referenzvariable ist . Für gewöhnliche Variablen ist der Wert 0 und für Referenzvariablen ist der Wert 1. Diese Variable wirkt sich auf die gemeinsame Nutzung, Trennung usw. von zval aus. Wir werden das später besprechen.

Wie der Name schon sagt, sind ref_count__gc und is_ref__gc zwei sehr wichtige Felder, die für den GC-Mechanismus von PHP erforderlich sind. Die Werte dieser beiden Felder können über xdebug usw. debuggt werden. Werkzeuge anzeigen.

Als nächstes konzentrieren wir uns auf zval, um den Speichermechanismus von PHP-Variablen zu beschreiben.

Ich habe die Installation von Xdebug auch im vorherigen PHPstorm Xdebug-Debugging vorgestellt. Ich werde hier nicht näher darauf eingehen: phpstorm+Xdebug Breakpoint Debugging PHP

安装成功后,你的脚本中,可以通过xdebug_debug_zval打印Zval的信息,用法:

 $var = 1;
 debug_zval_dump($var);
 $var_dup = $var;
 debug_zval_dump($var);
Nach dem Login kopieren

实例一:

    $a = 1;
    $b = $a;
    $c = $b;
    $d = &$c; // 在一堆非引用赋值中,插入一个引用
Nach dem Login kopieren

整个过程图示如下:

---------------------------------------------------------

实例二:

   $a = 1;
    $b = &$a;
    $c = &$b;
    $d = $c; // 在一堆引用赋值中,插入一个非引用
Nach dem Login kopieren

整个过程图示如下:



通过实例一、二,展现了,这就是PHP的copy on write写时分离机制change on write写时改变机制

过程:

PHP在修改一个变量以前,会首先查看这个变量的refcount,如果refcount大于1,PHP就会执行一个分离的例程,

对于上面的实例一代码,当执行到第四行的时候,PHP发现$c指向的zval的refcount大于1,那么PHP就会复制一个新的zval出来,将原zval的refcount减1,并修改symbol_table,使得$a,$b和$c分离(Separation)。这个机制就是所谓的copy on write(写时复制/写时分离)。把$d指向的新zval的is_ref的值 == 1 ,这个机制叫做change on write(写时改变)

结论:

分离指的是:分离两个变量存储的zval的位置,让分开不指向同一个空间! (那如何判定是否要分离呢,依据是什么?见下边)

改变指的是,有&引用赋值时,要把新开辟的zval 的 is_ref 赋值为1


判定是否分离的条件:如果is_ref =1 或recount == 1,则不分离

if((*val)->is_ref || (*val)->refcount<2){
          //不执行Separation
        ... ;//process
  }
Nach dem Login kopieren

---------------------------------------------------------------------------------------------------

实例三:(内存是如何泄漏的)

数组变量与普通变量生成的zval非常类似,但也有很大不同

举例:


$a = $array(&#39;one&#39;);  
$a[] = &$a;  
xdebug_debug_zval(&#39;a&#39;);
Nach dem Login kopieren


debug_zval_dump打印出zval的结构是:

a: (refcount=2, is_ref=1)=array (
    0 => (refcount=1, is_ref=0)=&#39;one&#39;, 
    1 => (refcount=2, is_ref=1)=...
)
Nach dem Login kopieren


上述输出中,…表示指向原始数组,因而这是一个循环的引用。如下图所示:




现在,我们对$a执行unset操作,这会在symbol table中删除相应的symbol,同时,zval的refcount减1(之前为2),也就是说,现在的zval应该是这样的结构:

unset($a);
(refcount=1, is_ref=1)=array (
    0 => (refcount=1, is_ref=0)=&#39;one&#39;, 
    1 => (refcount=1, is_ref=1)=...
)
Nach dem Login kopieren


(应该ref_count=1)


(unset,其实就是打断$a在 符号表(symble table) 与zval 的一个指针映射关系。)


这时,不幸的事情发生了!

  Unset之后,虽然没有变量指向该zval,但是该zval却不能被GC(指PHP5.3之前的单纯引用计数机制的GC)清理掉,$a 被释放,但是$a里的$a[1]也指向了该zval,它没有被释放,导致zval的refcount均大于0。这样,这些zval实际上会一直存在内存中,直到请求结束(参考SAPI的生命周期)。在此之前,这些zval占据的内存不能被使用,便白白浪费了,换句话说,无法释放的内存导致了内存泄露

Wenn diese Art von Speicherverlust nur einmal oder mehrmals auftritt, ist das nicht schlimm, aber wenn es sich um Tausende von Speicherverlusten handelt, ist es ein großes Problem. Da der Speicher insbesondere bei lang laufenden Skripten (z. B. Daemons, die immer ohne Unterbrechung im Hintergrund ausgeführt werden) nicht recycelt werden kann, steht dem System irgendwann „kein Speicher mehr zur Verfügung“, sodass dieser Vorgang vermieden werden muss.

Garbage-Collection-Mechanismus:

1.php verwendet ursprünglich einen Referenzzähler, um ein Speicherrecycling zu erreichen, das heißt, mehrere PHP-Variablen können auf denselben Speicher verweisen. In diesem Fall wird das Entfernen deaktiviert Einer von ihnen gibt den Speicher nicht frei sei
Automatische Reinigung$a = 1; $b = $a; unset($a);//$a开辟的内存不会回收
(enthält keine statischen Variablen, statische Variablen werden beim Laden des Skripts erstellt und freigegeben, wenn das Skript endet), Wenn Sie beispielsweise lokale Variablen innerhalb einer Funktion oder Methode deaktivieren, wird der Speicher bei der Betrachtung außerhalb der Funktion nicht reduziert. 3. Es liegt ein Fehler bei der Referenzzählung vor, das heißt, wenn ein Zirkelverweis auftritt, kann der Zähler nicht auf 0 zurückgesetzt werden.

Die Speichernutzung wird bis zum Ende fortgesetzt des Seitenzugriffs

.

Für dieses Problem wurde in PHP5.3 ein Garbage-Collection-Mechanismus hinzugefügt. Einzelheiten finden Sie in der Dokumentation: http://www.php.cn/

Der Garbage Collection-Mechanismus wurde erstmals in Lisp vorgeschlagen. Mehr über Müll Sammlungsinformationen.

Das obige ist der detaillierte Inhalt vonDetaillierte grafische Code-Erklärung des PHP-Kernel-Speichermechanismus (Trennung/Änderung). Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Verwandte Etiketten:
Quelle:php.cn
Erklärung dieser Website
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
Beliebte Tutorials
Mehr>
Neueste Downloads
Mehr>
Web-Effekte
Quellcode der Website
Website-Materialien
Frontend-Vorlage