Pourquoi s'intéresser à la mémoire en PHP ?
\nLa plupart des développeurs PHP considèrent la mémoire comme une ressource illimitée. Besoin d'un tableau d'un million d'éléments ? Pas de problème. Vous créez un objet et l'oubliez ? PHP nettoie tout derrière vous. Mais quand votre projet plante avec l'erreur Allowed memory size exhausted ou ralentit sans raison apparente, il est temps de comprendre ce qui se passe sous le capot.
Aujourd'hui, nous ouvrons la boîte noire de la gestion mémoire en PHP. Vous découvrirez comment PHP compte les références, pourquoi les références circulaires sont problématiques et comment faire du garbage collector votre allié. C'est parti !
\n\nComment PHP gère la mémoire : de la requête à la réponse
\nContrairement à C ou C++, où vous allouez et libérez la mémoire manuellement, PHP utilise le Zend Engine. Chaque processus PHP possède son propre pool mémoire. Lorsque vous écrivez :
\n$data = [1, 2, 3];\nPHP alloue de la mémoire pour le tableau, stocke les valeurs et incrémente le refcount (compteur de références). Tant qu'une variable référence $data, la mémoire reste allouée.
\nMais dès que vous faites :
\nunset($data);\nle compteur de références tombe à zéro, et le Zend Engine libère immédiatement la mémoire dans le pool. Simple et rapide. Cependant, il y a des subtilités…
\n\nL'arithmétique des références : refcount et is_ref
\nChaque variable en PHP est une structure zval qui contient la valeur, le type, le drapeau is_ref (est-ce une référence ?) et refcount. Regardons un exemple :
$a = "Bonjour";\n$b = $a; // refcount passe à 2 (copie lors de l'écriture)\n$b = "Monde"; // maintenant $a et $b sont des zval différents, refcount de "Bonjour" tombe à 1\n\nC'est ce qu'on appelle le copy-on-write (copie lors de l'écriture). Tant que vous ne modifiez pas la valeur, PHP économise la mémoire en utilisant la même zone. Mais dès que vous changez une variable, une copie est créée. Astucieux, non ?
\n\nLe garbage collector (GC) : quand refcount ne suffit pas
\nLe mécanisme classique de comptage de références fonctionne parfaitement dans 99% des cas. Mais il y a un problème : les références circulaires. Imaginez deux objets qui se référencent mutuellement :
\nclass Node {\n public $next;\n}\n\n$a = new Node();\n$b = new Node();\n$a->next = $b;\n$b->next = $a;\n\nunset($a);\nunset($b);\n\nAprès unset, les deux objets ont toujours un refcount de 1 (chacun référence l'autre). Bien qu'inaccessibles depuis le code, la mémoire n'est pas libérée. C'est une référence circulaire.
Avec beaucoup d'objets de ce type, la mémoire fuit. Pour y remédier, PHP a introduit un garbage collector complet (GC) à partir de la version 5.3.
\n\nComment fonctionne le GC en PHP ?
\nLe garbage collector ne s'exécute pas à chaque ligne de code. Il s'active lorsque le tampon racine (tableau de références circulaires potentielles) est plein. Par défaut, sa taille est de 10 000 éléments. Une fois le tampon plein, le GC lance un algorithme qui :
\n- \n
- Scanne les racines — trouve tous les zval avec un refcount > 0 pouvant faire partie d'un cycle. \n
- Diminue le refcount de tous les éléments enfants (comme si les références n'existaient pas). \n
- Vérifie — si après diminution le refcount est à zéro, c'est un « déchet » à supprimer. \n
- Restaure le refcount pour ceux qui ne sont pas des déchets et libère la mémoire. \n
Ce processus s'appelle le garbage collection. Vous pouvez le lancer manuellement :
\ngc_collect_cycles();\nMais évitez de l'appeler à tout bout de champ — c'est une opération coûteuse.
\n\nComment détecter les fuites mémoire en PHP ?
\nSi votre script s'exécute longtemps (ex : worker de file d'attente ou démon), les fuites deviennent critiques. Voici quelques outils