L'un des plus grands avantages de l'utilisation d'un langage de script est que vous pouvez profiter de son mécanisme automatique de récupération de place (libération de mémoire). Vous n'avez besoin d'effectuer aucun traitement pour libérer de la mémoire après avoir utilisé la variable, PHP le fera pour vous.
Bien sûr, nous pouvons appeler la fonction unset() pour libérer de la mémoire si nous le souhaitons, mais cela n’est généralement pas nécessaire.
Cependant, en PHP, il existe au moins une situation dans laquelle la mémoire ne sera pas automatiquement libérée, même si unset() est appelé manuellement. Les détails peuvent être trouvés sur : http://bugs.php.net/bug.php?id=33595 .
Symptômes du problème : S'il existe une relation de référence mutuelle entre deux objets, telle que "objet parent-objet enfant", l'appel d'unset() sur l'objet parent ne libérera pas la mémoire référençant l'objet parent dans l'objet enfant (même si l'objet parent l'objet est récupéré, ne fonctionne pas non plus).
Un peu confus ? Regardons le code suivant :
<?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";}?>Exécutez ce code et vous verrez l'utilisation de la mémoire. devient de plus en plus élevé jusqu'à ce qu'il soit épuisé.
...33,551,61633,551,97633,552,33633,552,696 Erreur fatale PHP : taille de mémoire autorisée de 33554432 octets épuisée (essai d'allouer 16 octets) dans memleak.php à la ligne 17. Pour la plupart des programmeurs PHP, c'est cette situation n'est pas un problème.
Mais si vous utilisez beaucoup d’objets qui se référencent les uns les autres dans un code de longue durée, surtout si les objets sont relativement volumineux, la mémoire s’épuisera rapidement.
La solution Userland est quelque peu fastidieuse et inélégante, mais le lien bugs.php.net mentionné précédemment fournit une solution.
Cette solution utilise une méthode destructrice avant de libérer l'objet pour atteindre cet objectif. La méthode Destructor peut effacer toutes les références internes à l'objet parent, ce qui signifie que cette partie de la mémoire qui autrement déborderait peut être libérée.
Voici le code "après correctif":
<?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";}?>Notez la nouvelle méthode Foo::__destruct() et l'appel à $foo->__destruct() avant de libérer l'objet. Maintenant, ce code résout le problème de l’augmentation de l’utilisation de la mémoire, afin que le code puisse bien fonctionner.
Solution du noyau PHP ?
Pourquoi un débordement de mémoire se produit-il ? Je ne maîtrise pas la recherche sur le noyau PHP, mais je suis sûr que ce problème est lié au comptage de références.
Le nombre de références de $foo référencé dans $bar ne sera pas décrémenté car l'objet parent $foo est libéré. À ce stade, PHP pense que vous avez toujours besoin de l'objet $foo, donc cette partie de la mémoire ne sera pas libérée. . ou alors.
Mon ignorance se voit vraiment ici, mais l'idée générale est la suivante : un décompte de références n'est pas décrémenté, donc de la mémoire n'est jamais libérée.
Dans le lien bugs.php.net susmentionné, j'ai vu que modifier le processus de récupération de place sacrifierait d'énormes performances, et comme je ne connais pas grand-chose au comptage de références, j'ai supposé que c'était vrai.
Au lieu de modifier le processus de garbage collection, pourquoi ne pas unset() pour libérer les objets internes ? (Ou appeler __destruct() lors de la libération de l'objet ?)
Peut-être que les développeurs du noyau PHP peuvent apporter des modifications à ce mécanisme de garbage collection ici ou ailleurs.
Mise à jour : Martin Fjordvald a mentionné dans les commentaires un patch écrit par David Wang pour le ramasse-miettes (en fait, il ressemble plus à "un morceau de tissu entier" - très énorme. Voir les informations d'exportation CVS à la fin de cet e-mail pour plus de détails.) En effet existe (un e-mail) et a retenu l'attention des membres de la communauté de développement du noyau PHP. La question est de savoir si ce patch doit être intégré à PHP5.3 et il n'a pas reçu beaucoup de support. Je pense qu'un bon compromis consiste à appeler la méthode __destruct() dans l'objet dans la fonction unset() ;