Einer der größten Vorteile der Verwendung einer Skriptsprache besteht darin, dass Sie den automatischen Garbage-Collection-Mechanismus (Speicherfreigabe) nutzen können. Sie müssen keine Verarbeitung durchführen, um den Speicher freizugeben, nachdem Sie die Variable verwendet haben. PHP übernimmt dies für Sie.
Natürlich können wir bei Bedarf die Funktion unset() aufrufen, um den Speicher freizugeben, aber normalerweise ist dies nicht erforderlich.
Allerdings gibt es in PHP mindestens eine Situation, in der der Speicher nicht automatisch freigegeben wird, selbst wenn unset() manuell aufgerufen wird. Details finden Sie unter: http://bugs.php.net/bug.php?id=33595 .
Problemsymptome: Wenn zwischen zwei Objekten eine gegenseitige Referenzbeziehung besteht, z. B. „übergeordnetes Objekt-untergeordnetes Objekt“, wird durch den Aufruf von unset() für das übergeordnete Objekt der auf das übergeordnete Objekt verweisende Speicher im untergeordneten Objekt nicht freigegeben (selbst wenn das übergeordnete Objekt vorhanden ist). Objekt wird im Garbage Collection gesammelt, funktioniert auch nicht).
Etwas verwirrt? Schauen wir uns den folgenden Code an:
<?phpclass Foo {function __construct(){$this->bar = new Bar($this);}}class Bar {function __construct($foo = null){$this-> foo = $foo;}}while (true) {$foo = new Foo();unset($foo);echo number_format(memory_get_usage()) "n";}?>Führen Sie diesen Code aus und Sie werden die Speichernutzung sehen wird immer höher, bis es aufgebraucht ist.
...33,551,61633,551,97633,552,33633,552,696PHP Schwerwiegender Fehler: Zulässige Speichergröße von 33554432 Bytes erschöpft (versucht, 16 Bytes zuzuweisen) in memleak.php in Zeile 17 Für die meisten PHP-Programmierer ist dies diese Situation ist kein Problem.
Wenn Sie jedoch in einem lang laufenden Code viele Objekte verwenden, die aufeinander verweisen, insbesondere wenn die Objekte relativ groß sind, wird der Speicher schnell erschöpft sein.
Die Userland-Lösung ist etwas mühsam und unelegant, aber der zuvor erwähnte Link bugs.php.net bietet eine Lösung.
Um dieses Ziel zu erreichen, verwendet diese Lösung vor der Freigabe des Objekts eine Destruktormethode. Die Destructor-Methode kann alle internen übergeordneten Objektreferenzen löschen, was bedeutet, dass dieser Teil des Speichers, der sonst überlaufen würde, freigegeben werden kann.
Hier ist der „After-Fix“-Code:
<?phpclass Foo {function __construct(){$this->bar = new Bar($this);}function __destruct(){unset($this->bar);}}class Bar {function __construct($foo = null){$this->foo = $foo;}}while (true) {$foo = new Foo();$foo->__destruct();unset($foo);echo number_format(memory_get_usage()) "n";}?>Beachten Sie die neue Methode Foo::__destruct() und den Aufruf von $foo->__destruct() vor der Freigabe des Objekts. Dieser Code löst nun das Problem der erhöhten Speichernutzung, sodass der Code gut funktionieren kann.
PHP-Kernel-Lösung?
Warum kommt es zu einem Speicherüberlauf? Ich bin nicht bewandert in der PHP-Kernel-Recherche, bin mir aber sicher, dass dieses Problem mit der Referenzzählung zusammenhängt.
Der Referenzzähler von $foo, auf den in $bar verwiesen wird, wird nicht verringert, da das übergeordnete Objekt $foo freigegeben wird. Zu diesem Zeitpunkt geht PHP davon aus, dass Sie das $foo-Objekt noch benötigen, sodass dieser Teil des Speichers nicht freigegeben wird. . oder so.
Hier zeigt sich wirklich meine Unwissenheit, aber die allgemeine Idee ist: Ein Referenzzähler wird nicht dekrementiert, sodass nie etwas Speicher freigegeben wird.
Im oben genannten Link zu bugs.php.net habe ich gesehen, dass eine Änderung des Garbage-Collection-Prozesses enorme Leistungseinbußen mit sich bringen würde, und da ich nicht viel über Referenzzählung weiß, bin ich davon ausgegangen, dass dies wahr ist.
Anstatt den Garbage-Collection-Prozess zu ändern, warum nicht mit unset() die internen Objekte freigeben? (Oder beim Freigeben des Objekts __destruct() aufrufen?)
Vielleicht können PHP-Kernel-Entwickler hier oder anderswo Änderungen an diesem Garbage-Collection-Mechanismus vornehmen.
Update: Martin Fjordvald erwähnte in den Kommentaren einen von David Wang geschriebenen Patch für die Garbage Collection (eigentlich sieht er eher aus wie „ein ganzes Stück Stoff“ – sehr riesig. Weitere Informationen finden Sie in den CVS-Exportinformationen am Ende dieser E-Mail.) In der Tat existiert (eine E-Mail) und hat die Aufmerksamkeit von Mitgliedern der PHP-Kernel-Entwicklungsgemeinschaft erhalten. Die Frage ist, ob dieser Patch in PHP5.3 eingefügt werden sollte, und er hat nicht viel Unterstützung erhalten. Ich denke, ein guter Kompromiss besteht darin, die Methode __destruct() im Objekt in der Funktion unset() aufzurufen;