Uno de los mayores beneficios de utilizar un lenguaje de scripting es que puede aprovechar su mecanismo automático de recolección de basura (liberando memoria). No necesita realizar ningún procesamiento para liberar la memoria después de usar la variable, PHP lo hará por usted.
Por supuesto, podemos llamar a la función unset() para liberar la memoria si queremos, pero normalmente no es necesario hacerlo.
Sin embargo, en PHP, hay al menos una situación en la que la memoria no se liberará automáticamente, incluso si se llama manualmente a unset(). Los detalles se pueden encontrar en: http://bugs.php.net/bug.php?id=33595 .
Síntomas del problema: si existe una relación de referencia mutua entre dos objetos, como "objeto principal-objeto secundario", llamar a unset() en el objeto principal no liberará la memoria que hace referencia al objeto principal en el objeto secundario (incluso si el objeto principal El objeto se recolecta como basura, tampoco funciona).
¿Un poco confundido? Veamos el siguiente código:
<?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";}?>Ejecute este código y verá el uso de memoria. aumenta cada vez más hasta que se agota.
...33,551,61633,551,97633,552,33633,552,696PHP Error fatal: el tamaño de memoria permitido de 33554432 bytes se agotó (se intentó asignar 16 bytes) en memleak.php en la línea 17. Para la mayoría de los programadores de PHP, esta es esta situación no es un problema.
Pero si utiliza muchos objetos que hacen referencia entre sí en un código de ejecución larga, especialmente si los objetos son relativamente grandes, la memoria se agotará rápidamente.
La solución Userland es algo tediosa y poco elegante, pero el enlace bugs.php.net mencionado anteriormente proporciona una solución.
Esta solución utiliza un método destructor antes de liberar el objeto para lograr este objetivo. El método Destructor puede borrar todas las referencias internas a objetos principales, lo que significa que esta parte de la memoria que de otro modo se desbordaría se puede liberar.
Aquí está el código "después de la corrección":
<?phpclass Foo {function __construct(){$this->bar = new Bar($this);}function __destruct(){unset($this->bar);}}class Barra {función __construct($foo = null){$this->foo = $foo;}}mientras (verdadero) {$foo = new Foo();$foo->__destruct();unset($foo);echo number_format(memory_get_usage()) . "n";}?>Tenga en cuenta el nuevo método Foo::__destruct() y la llamada a $foo->__destruct() antes de liberar el objeto. Ahora este código resuelve el problema del aumento del uso de memoria, por lo que el código puede funcionar bien.
¿Solución del núcleo PHP?
¿Por qué ocurre el desbordamiento de la memoria? No soy experto en la investigación del kernel de PHP, pero estoy seguro de que este problema está relacionado con el recuento de referencias.
El recuento de referencias de $foo al que se hace referencia en $bar no disminuirá porque el objeto principal $foo está liberado. En este momento, PHP cree que todavía necesita el objeto $foo, por lo que esta parte de la memoria no se liberará. .o así.
Mi ignorancia realmente se nota aquí, pero la idea general es: un recuento de referencias no disminuye, por lo que nunca se libera parte de la memoria.
En el enlace bugs.php.net antes mencionado vi que modificar el proceso de recolección de basura sacrificaría un gran rendimiento y, como no sé mucho sobre el recuento de referencias, supuse que esto era cierto.
En lugar de cambiar el proceso de recolección de basura, ¿por qué no unset() para liberar los objetos internos? (¿O llamar a __destruct() al liberar el objeto?)
Quizás los desarrolladores del kernel PHP puedan realizar cambios en este mecanismo de recolección de basura aquí o en otro lugar.
Actualización: Martin Fjordvald mencionó en los comentarios un parche escrito por David Wang para la recolección de basura (en realidad, parece más bien "un trozo de tela entero", muy grande. Consulte la información de exportación de CVS al final de este correo electrónico para obtener más detalles). existe (un correo electrónico) y ha recibido atención de miembros de la comunidad de desarrollo del kernel PHP. La pregunta es si este parche debería incluirse en PHP5.3 y no ha recibido mucho soporte. Creo que un buen compromiso es llamar al método __destruct() en el objeto en la función unset();