Um dos maiores benefícios de usar uma linguagem de script é que você pode aproveitar seu mecanismo automático de coleta de lixo (liberando memória). Você não precisa fazer nenhum processamento para liberar memória após usar a variável, o PHP fará isso por você.
Claro, podemos chamar a função unset() para liberar memória se quisermos, mas geralmente não há necessidade de fazer isso.
Porém, no PHP, existe pelo menos uma situação em que a memória não será liberada automaticamente, mesmo que unset() seja chamado manualmente. Detalhes podem ser encontrados em: http://bugs.php.net/bug.php?id=33595 .
Sintomas do problema: Se houver um relacionamento de referência mútua entre dois objetos, como "objeto pai-objeto filho", chamar unset() no objeto pai não liberará a memória que faz referência ao objeto pai no objeto filho (mesmo que o pai objeto é coletado como lixo, também não funciona).
Um pouco confuso? Vejamos o seguinte 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";}?>Execute este código e você verá o uso de memória. fica cada vez mais alto até se esgotar.
...33.551.61633.551.97633.552.33633.552.696PHP Erro fatal: tamanho de memória permitido de 33554432 bytes esgotado (tentou alocar 16 bytes) em memleak.php na linha 17 Para a maioria dos programadores PHP, esta é esta situação não é um problema.
Mas se você usar muitos objetos que fazem referência entre si em um código de longa execução, especialmente se os objetos forem relativamente grandes, a memória se esgotará rapidamente.
A solução Userland é um tanto tediosa e deselegante, mas o link bugs.php.net mencionado anteriormente fornece uma solução.
Esta solução usa um método destruidor antes de liberar o objeto para atingir esse objetivo. O método Destructor pode limpar todas as referências internas do objeto pai, o que significa que esta parte da memória que de outra forma transbordaria pode ser liberada.
Aqui está o código "após correção":
<?phpclass Foo {function __construct(){$this->bar = new Bar($this);}function __destruct(){unset($this->bar);}}class Barra {function __construct($foo = null){$this->foo = $foo;}}while (true) {$foo = new Foo();$foo->__destruct();unset($foo);echo number_format(memory_get_usage()) . "n";}?>Observe o novo método Foo::__destruct() e a chamada para $foo->__destruct() antes de liberar o objeto. Agora, esse código resolve o problema de aumentar o uso de memória, para que o código possa funcionar bem.
Solução de kernel PHP?
Por que ocorre o estouro de memória? Não sou proficiente em pesquisa do kernel PHP, mas tenho certeza de que esse problema está relacionado à contagem de referências.
A contagem de referência de $foo referenciada em $bar não será decrementada porque o objeto pai $foo foi liberado. Neste momento, o PHP pensa que você ainda precisa do objeto $foo, então esta parte da memória não será liberada. . ou algo assim.
Minha ignorância realmente fica evidente aqui, mas a ideia geral é: uma contagem de referência não é decrementada, portanto alguma memória nunca é liberada.
No link bugs.php.net mencionado acima, vi que modificar o processo de coleta de lixo sacrificaria um enorme desempenho e, como não sei muito sobre contagem de referências, presumi que isso fosse verdade.
Em vez de alterar o processo de coleta de lixo, por que não unset() para liberar os objetos internos? (Ou chame __destruct() ao liberar o objeto?)
Talvez os desenvolvedores do kernel PHP possam fazer alterações neste mecanismo de coleta de lixo aqui ou em outro lugar.
Atualização: Martin Fjordvald mencionou nos comentários um patch escrito por David Wang para coleta de lixo (na verdade, parece mais "um pedaço inteiro de pano" - muito grande. Veja as informações de exportação do CVS no final deste e-mail para obter detalhes). existe (um e-mail) e recebeu atenção de membros da comunidade de desenvolvimento do kernel PHP. A questão é se este patch deve ser colocado no PHP5.3 e não recebeu muito suporte. Acho que um bom compromisso é chamar o método __destruct() no objeto na função unset();