There seems to be no way to inspect the reference count of a specific class variable but you can view the reference count of all variables in the current class instance with xdebug_debug_zval('this');Une variable PHP est stockée en interne dans un conteneur appelé "zval". Un conteneur zval contient, outre le type de la variable et sa valeur, deux informations supplémentaires. La premiÚre se nomme "is_ref" et est une valeur booléenne qui indique si une variable fait partie d'une référence ou non. Grùce à cette information, le moteur de PHP sait différencier les variables normales des références. Comme PHP autorise le programmeur à utiliser des références, au moyen de l'opérateur &, un conteneur zval possÚde aussi un mécanisme de comptage des références afin d'optimiser l'utilisation de la mémoire. Cette seconde information, appelée "refcount", contient le nombre de variables (aussi appelées symboles) qui pointent vers ce conteneur zval. Tous les symboles sont stockés dans une table de symboles, et il y a une table par espace de visibilité (scope). Il y a un espace global pour le script principal (celui appelé par exemple via le navigateur) et un espace par fonction ou méthode.
Un conteneur zval est créé lorsqu'une nouvelle variable est créée avec une valeur constante, par exemple :
Exemple #1 Création d'un nouveau conteneur zval
<?php
$a = "new string";
?>
Dans ce cas, le nouveau symbole a est créé dans le scope courant,
et un nouveau conteneur est créé avec comme type string et comme valeur
new string. Le bit "is_ref" est mis par défaut à false car aucune
rĂ©fĂ©rence n'a Ă©tĂ© créée par le programmeur. Le compteur de rĂ©fĂ©rences "refcount" est mis Ă
1 car il n'y a qu'un seul symbole qui utilise ce conteneur.
Il est à noter que les références (c.-à -d. "is_ref" est true) avec "refcount"
1, sont traitées comme si elles n'étaient pas des
références (c.-à -d. comme si "is_ref" était false). Si
» Xdebug est installé, il est possible d'afficher cette
information en appelant xdebug_debug_zval().
Exemple #2 Affichage des informations zval
<?php
$a = "new string";
xdebug_debug_zval('a');
?>L'exemple ci-dessus va afficher :
a: (refcount=1, is_ref=0)='new string'
Assigner cette variable à un autre symbole va incrémenter le refcount.
Exemple #3 Incrémentation du refcount d'une zval
<?php
$a = "new string";
$b = $a;
xdebug_debug_zval( 'a' );
?>L'exemple ci-dessus va afficher :
a: (refcount=2, is_ref=0)='new string'
Le refcount vaut 2 ici, car le mĂȘme conteneur est liĂ© Ă
la fois Ă a et Ă b. PHP est suffisamment
intelligent pour ne pas dupliquer le conteneur lorsque ce n'est pas nécessaire.
Les conteneurs sont détruits lorsque leur "refcount" atteint zéro. Le
"refcount" est dĂ©crĂ©mentĂ© de un lorsque n'importe quel symbole liĂ© Ă
un conteneur sort du scope (ex. : lorsque la fonction se termine) ou
lorsqu'un symbole est déalloué (ex. : par l'appel de unset()).
L'exemple qui suit le démontre :
Exemple #4 Décrémentation du refcount d'une zval
<?php
$a = "new string";
$c = $b = $a;
xdebug_debug_zval( 'a' );
$b = 42;
xdebug_debug_zval( 'a' );
unset( $c );
xdebug_debug_zval( 'a' );
?>L'exemple ci-dessus va afficher :
a: (refcount=3, is_ref=0)='new string' a: (refcount=2, is_ref=0)='new string' a: (refcount=1, is_ref=0)='new string'
Si, maintenant, nous appelons unset($a);, le conteneur zval, incluant
le type et la valeur, va ĂȘtre supprimĂ© de la mĂ©moire.
Les choses se compliquent dans le cas de types composés comme array et object. à la différence des valeurs scalar, les array et object stockent leurs propriétés dans une table de symboles qui leur est propre. Ceci signifie que l'exemple qui suit crée trois conteneurs zval :
Exemple #5 Création d'une zval array
<?php
$a = array( 'meaning' => 'life', 'number' => 42 );
xdebug_debug_zval( 'a' );
?>Résultat de l'exemple ci-dessus est similaire à :
a: (refcount=1, is_ref=0)=array ( 'meaning' => (refcount=1, is_ref=0)='life', 'number' => (refcount=1, is_ref=0)=42 )
Ou graphiquement
Les trois conteneurs zval sont : a, meaning, et number. Les mĂȘmes rĂšgles s'appliquent pour l'incrĂ©mentation et la dĂ©crĂ©mentation des "refcounts". Ci-aprĂšs, nous ajoutons un autre Ă©lĂ©ment au tableau, et nous renseignons sa valeur avec le contenu d'un Ă©lĂ©ment dĂ©jĂ existant du tableau :
Exemple #6 Ajout d'un élément déjà existant au tableau
<?php
$a = array( 'meaning' => 'life', 'number' => 42 );
$a['life'] = $a['meaning'];
xdebug_debug_zval( 'a' );
?>Résultat de l'exemple ci-dessus est similaire à :
a: (refcount=1, is_ref=0)=array ( 'meaning' => (refcount=2, is_ref=0)='life', 'number' => (refcount=1, is_ref=0)=42, 'life' => (refcount=2, is_ref=0)='life' )
Ou graphiquement
La sortie Xdebug que nous voyons indique que l'ancien et le nouvel élément du tableau
pointent maintenant tous deux vers un conteneur zval dont le "refcount" vaut 2.
MĂȘme si la sortie XDebug montre deux conteneurs zval avec comme valeur 'life', ils sont les
mĂȘmes. La fonction xdebug_debug_zval() ne montre pas cela, mais cela
pourrait ĂȘtre constatĂ© en affichant aussi le pointeur de mĂ©moire.
Supprimer un élément du tableau est assimilable à la suppression d'un symbole depuis un espace. Ce faisant, le "refcount" du conteneur vers lequel l'élément du tableau pointe est décrémenté. Une fois encore, s'il atteint zéro, le conteneur zval est supprimé de la mémoire. Voici un exemple qui le démontre :
Exemple #7 Suppression d'un élément de tableau
<?php
$a = array( 'meaning' => 'life', 'number' => 42 );
$a['life'] = $a['meaning'];
unset( $a['meaning'], $a['number'] );
xdebug_debug_zval( 'a' );
?>Résultat de l'exemple ci-dessus est similaire à :
a: (refcount=1, is_ref=0)=array ( 'life' => (refcount=1, is_ref=0)='life' )
Maintenant, les choses deviennent intĂ©ressantes si nous ajoutons le tableau comme Ă©lĂ©ment de lui-mĂȘme. Nous faisons cela dans l'exemple qui suit, en utilisant un opĂ©rateur de rĂ©fĂ©rence pour Ă©viter que PHP ne crĂ©e une copie :
Exemple #8 Ajout du tableau comme rĂ©fĂ©rence Ă lui-mĂȘme en tant qu'Ă©lĂ©ment
<?php
$a = array( 'one' );
$a[] =& $a;
xdebug_debug_zval( 'a' );
?>Résultat de l'exemple ci-dessus est similaire à :
a: (refcount=2, is_ref=1)=array ( 0 => (refcount=1, is_ref=0)='one', 1 => (refcount=2, is_ref=1)=... )
Ou graphiquement
On peut voir que la variable tableau (a) tout comme le second élément
(1) pointent désormais vers un conteneur dont le "refcount" vaut
2. Les "..." sur l'affichage indiquent une récursion, qui, dans ce cas,
signifie que le "..." pointe sur le tableau lui-mĂȘme.
Comme prĂ©cĂ©demment, supprimer une variable supprime son symbole, et le refcount du conteneur sur lequel il pointait est dĂ©crĂ©mentĂ©. Donc, si nous supprimons la variable $a aprĂšs avoir exĂ©cutĂ© le code ci-dessus, le compteur de rĂ©fĂ©rences du conteneur sur lequel pointent $a et l'Ă©lĂ©ment "1" sera dĂ©crĂ©mentĂ© de un, passant de "2" Ă "1". Ceci peut ĂȘtre reprĂ©sentĂ© par :
Exemple #9 Suppression de $a
(refcount=1, is_ref=1)=array ( 0 => (refcount=1, is_ref=0)='one', 1 => (refcount=1, is_ref=1)=... )
Ou graphiquement
Bien qu'il n'y ait plus aucun symbole dans l'espace de variables courant qui pointe vers cette structure, elle ne peut ĂȘtre nettoyĂ©e, car l'Ă©lĂ©ment "1" du tableau pointe toujours vers ce mĂȘme tableau. Comme il n'y a plus aucun symbole externe pointant vers cette structure, l'utilisateur ne peut pas la nettoyer manuellement ; il y a donc une fuite de mĂ©moire. Heureusement, PHP va dĂ©truire cette structure Ă la fin de la requĂȘte, mais avant cette Ă©tape, la mĂ©moire n'est pas libĂ©rĂ©e. Cette situation se produit souvent lors de l'implĂ©mentation d'un algorithme d'analyse ou d'autres idĂ©es oĂč l'on a un enfant qui pointe vers son parent. La mĂȘme chose peut bien entendu se produire avec les objets, et c'est mĂȘme plus probable, puisqu'ils sont toujours implicitement utilisĂ©s par "rĂ©fĂ©rence".
Ceci peut ne pas ĂȘtre gĂȘnant si cela n'arrive qu'une ou deux fois, mais s'il y a des milliers, ou mĂȘme des millions, de ces fuites mĂ©moires, alors cela risque Ă©videmment de devenir un problĂšme important. C'est particuliĂšrement problĂ©matique pour les scripts qui durent longtemps, comme les dĂ©mons pour lesquels la requĂȘte ne termine pour ainsi dire jamais, ou encore dans de grosses suites de tests unitaires. Ce dernier cas a Ă©tĂ© rencontrĂ© en lançant les tests unitaires du composant Template de la bibliothĂšque eZ Components. Dans certains cas, la suite de tests nĂ©cessitait plus de 2Go de mĂ©moire, que le serveur de test n'avait pas vraiment Ă disposition.
There seems to be no way to inspect the reference count of a specific class variable but you can view the reference count of all variables in the current class instance with xdebug_debug_zval('this');Result of "Example #8 Adding the array itself as an element of it self" will be another for PHP7:
a: (refcount=2, is_ref=1)=array (
0 => (refcount=2, is_ref=0)='one',
1 => (refcount=2, is_ref=1)=...
)
insted of:
a: (refcount=2, is_ref=1)=array (
0 => (refcount=1, is_ref=0)='one',
1 => (refcount=2, is_ref=1)=...
)
Internal value representation in PHP 7:
https://nikic.github.io/2015/05/05/Internal-value-representation-in-PHP-7-part-1.htmlIf a variable is not present in the current scope xdebug_debug_zvalăwill return null.$a = 'new string';
$b = 1;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
ouputs with PHP 7.3.12 (cli)
a: (interned, is_ref=0)='new string'
b: (refcount=0, is_ref=0)=1my php versoin : HP 7.1.25 (cli) (built: Dec 7 2018 08:20:45) ( NTS )
$a = 'new string';
$b = 1;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
output:
a: (refcount=2, is_ref=0)='new string'
b: (refcount=0, is_ref=0)=1
if $a is a string value, 'refcount' equal 2 by defalut.my php version is PHP 7.1.6 (cli), when I run
$a = 'new string';
$b = 1;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
it shows:
a: (refcount=0, is_ref=0)='new string'
b: (refcount=0, is_ref=0)=1