Ramasse-miettes (Garbage Collection)
PHP Manual

Cycles de nettoyage

Traditionnellement, les mécanismes de comptage des références comme utilisés dans PHP auparavant ne pouvaient détecter les fuites mémoires dûes à des références circulaires. Depuis PHP 5.3.0, un algorithme synchrone issue de l'analyse » Concurrent Cycle Collection in Reference Counted Systems est utilisé pour répondre à ce problème particulier.

Une explication complète du fonctionnement de l'algorithme serait hors de portée pour cette section, mais nous allons ici détailler les principes de base. D'abord, nous allons établir quelques règles. Si un refcount est incrémenté, le conteneur est toujours utilisé, donc pas nettoyé. Si le refcount est décrémenté et atteint zéro, le conteneur zval peut être supprimé et la mémoire libérée. Ceci signifie que les cycles de nettoyages ne peuvent intervenir que lorsque le refcount est décrémenté vers une valeur différente de zéro. Ensuite, dans un cycle de nettoyage, il est possible de détecter les déchets en vérifiant s'il est possible ou non de décrémenter leur refcount de une unité en vérifiant quelles zvals ont un refcount à zéro.

Algorithme de collecte des déchets

Pour éviter d'appeler la routine de nettoyage à chaque décrémentation de refcount possible, l'algorithme place toutes les zval racines dans un "tampon de racines" (en les marquant). Il s'assure aussi que chaque racine n'apparait qu'une seule fois dans le tampon. Le mécanisme de nettoyage intervient alors lorsque le tampon est plein. Voyez l'étape A sur la figure ci-dessus.

A l'étape B, l'algorithme lance une recherche sur toutes les racines possibles afin de décrémenter de une unité les refcounts de toutes les zvals qu'il trouve, en faisant bien attention de ne pas décrémenter 2 fois le refcount de la même zval (en les marquant comme "grises"). Dans l'étape C, l'algorithme relance une recherche sur toutes les racines possibles et scrute la valeur de refcount de chaque zval. S'il trouve un refcount à zéro, la zval est marquée comme "blanche" (bleu sur la figure). S'il trouve autre chose que zéro, il annule sa décrémentation en refaisant une recherche à partir de ce noeud, et les marque comme "noires" à nouveau. Dans la dernière étape, D, l'algorithme parcours tout le tampon des racines et les supprime, tout en scrutant chaque zval, toute zval marquée comme "blanche" à l'étape précédente est alors supprimée de la mémoire.

Maintenant que vous savez globalement comment fonctionne l'algorithme, nous allons voir comment il a été intégré dans PHP. Par défaut, le ramasse-miettes de PHP est activé. Il existe cependant une options de php.ini pour changer cela : zend.enable_gc.

Lorsque le ramasse-miettes est activé, l'algorithme de recherche des cycles tel que décrit au dessus est exécuté à chaque fois que le tampon est plein. Le tampon de racines a une taille fixée à 10.000 racines (ce paramètre est changeable grâce à GC_ROOT_BUFFER_MAX_ENTRIES dans Zend/zend_gc.c dans le code source de PHP, une recompilation est donc nécessaire). Si le ramasse- miettes est désactivé, la recherche des cycles l'est aussi. Cependant, les racines probables seront toujours enregistrées dans le tampon, ceci ne dépend pas de l'activation du ramasse-miettes.

Si le tampon est plein alors que le mécanisme est désactivé, alors plus aucune racine ne pourra y être insérée. Ces racines ne seront donc pas analysées par l'algorithme au besoin, et si elles représentaient des références circulaires, il y aura fuite mémoire.

La raison pour laquelle les racines possibles sont tout de même enregistrées dans le tampon même si le mécanisme est désactivé est qu'il aurait été trop couteux de vérifier l'activation éventuelle du mécanisme à chaque tentative d'ajout d'une racine dans le tampon. Le mécanisme de ramasse-miettes et d'analyse peut être lui, couteux en temps.

En plus de pouvoir changer la valeur du paramètre de configuration zend.enable_gc, vous pouvez aussi activer ou désactiver le mécanisme de ramasse-miettes en appelant les fonctions gc_enable() ou gc_disable() respectivement. Ceci aura le même effet que de modifier le paramètre de configuration. Vous avez de plus la possibilité de forcer le passe du ramasse-miettes à un moment donné dans votre script, même si le tampon n'est pas encore complètement plein. Utilisez pour cela la fonction gc_collect_cycles(), cette fonction retournera le nombre de cycles alors collectés.

Vous pouvez prendre le contrôle en désactivant le ramasse-miettes ou en le forçant à passer à un moment donné car certaines parties de votre application peuvent être gourmandes en temps de traitement auquel cas vous pouvez désactiver le ramasse-miettes. Vous risquez à ce moment là des fuites mémoires. Vous pourriez vouloir déclencher manuellement le processus grâce à gc_collect_cycles() juste avant l'appel à gc_disable() pour libérer de la mémoire. Ceci laissera un tampon vidé et il y aura plus d'espace pour des racines probables (surtout lorsque le mécanisme est désactivé).


Ramasse-miettes (Garbage Collection)
PHP Manual