1
votes

PHP - pourquoi le passage par référence utilise-t-il la même quantité de mémoire?

La mémoire est épuisée dans mon script PHP. Je ne peux pas comprendre pourquoi. La limite est de 128 Mo. J'envoie, à partir d'un javascript xmlrequest (), une chaîne json d'un maximum de 10 Mo (généralement moins de 1 Mo). Le script PHP lui-même fait 43K. Même en copiant tout une douzaine de fois, je ne devrais pas manquer de mémoire. Je fais quelques appels à la base de données à getParentFolders () mais cela ne produit que quelques lignes. Certainement pas les 62 mégaoctets qu'il prétend utiliser. J'ai utilisé Xdebug (voir image) mais cela ne me dit rien d'utile, seulement que oui, j'utilise beaucoup de mémoire.

Donc, à ce stade, j'essaie de faire des «meilleures pratiques» pour minimiser l'utilisation de la mémoire. Une solution simple, ou du moins je pensais, était de transmettre des valeurs par référence. J'ai donc mis un «&» avant chaque variable dans chaque fonction. À ma grande surprise, il n'y a eu aucun changement dans la consommation de mémoire. C'était légèrement pire de quelques octets, en fait. J'ai également essayé d'utiliser chaque variable comme une variable globale, mais encore une fois, à ma grande surprise, il y avait peu de différence.

Alors, que se passe-t-il? Pourquoi le passage par référence et l'utilisation de globaux ne produisent-ils pas les avantages de performance attendus? (voir images)

Xdebug 'pass by value'

 entrez la description de l'image ici

Xdebug 'passe par référence'

 enter image description here

Notez qu'ils sont à peu près identiques.

Pour ceux qui veulent du code, voici les getParentFolders ( ), qui renvoie juste une courte chaîne mais utilise en quelque sorte 70 Mo!

function getParentFolders(&$node) {  //returns a string of folders
    debugprint("GetParentFOlders()");  //prints if $DEBUG flag is on
    $parent = getParent($node);
    $path = "";
    while ($parent) {  //goes till we hit root folder
        $path = $parent->title . '/' . $path;  //prepend it
        $parent = getParent($parent);
    }
    return $path;
}

function getParent(&$node) {  //return node that is the parent 
    global $email;
    $parentId = $node->parentId;
    $clientId = $node->parentClient;
    $idCol = $clientId . "_id";
    $tablename = $email . "_bookmarks";
    $query = "SELECT * FROM `$tablename`
                        WHERE $idCol = '$parentId'";  //only return one row since id is unique
    $result = sendquery($query);
    return (object) $result[0];
}

Edit:

Juste pour clarifier, je cherche une explication technique de L'utilisation de la mémoire PHP et les meilleures pratiques - en particulier la raison pour laquelle je ne vois pas de différences de mémoire - ne constituent pas une solution de contournement pour le problème.


2 commentaires

PHP, et la plupart des langages, utilise le ramasse-miettes. Il n'est pas garanti de libérer dès qu'une fonction est terminée. Qu'est-ce qui vous fait penser que la chaîne est le problème? Le problème peut être quelque part dans sendquery ou potentiellement combien de fois getParent est appelé.


C'est peut-être une référence circulaire dans vos données qui provoque une boucle infinie dans votre code. Votre nœud racine a-t-il un parent en lui-même?


3 Réponses :


0
votes

Assurez-vous que le résultat de la requête renvoie une ligne en utilisant LIMIT 1 ; Si aucun résultat n'est trouvé, alors faites en sorte que getParent () renvoie false ou null afin qu'il puisse être défini sur $ parent pour quitter la boucle while . Et je ne pense pas que vous ayez besoin de passer l'argument $ node par référence dans votre cas.


0 commentaires

0
votes

La valeur de $ node est une référence, c'est-à-dire un pointeur vers un objet. En PHP 5+, les "objets" ne sont pas des valeurs directement. Vous travaillez toujours avec des objets via des pointeurs vers eux: lorsque vous faites new ClassName , il s'évalue en un pointeur vers un objet; lorsque vous utilisez l'opérateur -> , il prend un pointeur vers un objet sur le côté gauche.

Donc, si ces deux fonctions étaient pass-par-valeur, la taille de l'objet passé est juste la taille d'un pointeur. Lorsque vous passez par référence, cela encapsule essentiellement le pointeur dans un autre niveau de pointeur, qui est de la même taille (bien que la mémoire totale soit supérieure car il y a deux pointeurs). Le seul point de passage par référence est qu'il vous permet d'assigner à $ node à l'intérieur de la fonction pour pointer vers un objet différent ou contenir un type de valeur différent, et le faire refléter dans la variable passée dans la fonction appelante, mais vous n'affectez pas à $ node nulle part ici (la modification d'un champ de l'objet pointé vers $ node n'est pas la même chose que l'attribution à $ node ), donc le passage par référence est inutile ici.

(De plus, même si vous avez passé un type valeur, comme les tableaux en PHP, ils ont une sémantique de copie sur écriture, et ne sont pas copiés tant que vous n'y avez pas écrit. Et même si vous y avez écrit et fait une copie, cette copie ne durerait que pendant la durée de vie de cette variable locale, qui se termine à la fin de la fonction, donc elle ne créerait pas d'utilisation persistante de la mémoire.)


4 commentaires

vous dites que c'était toujours passant par référence, quoi qu'il arrive?


@johnktejik: Non, c'est pass-by-value sans le &


Mmm ... vous avez dit que les objets sont toujours passés par référence, c'est-à-dire des pointeurs.


@johnktejik: Non, j'ai dit que les "objets" ne sont pas passés car les "objets" ne sont pas des valeurs en PHP5. Lorsque vous créez un objet par new SomeClass () , le type du résultat est une référence, c'est-à-dire un pointeur vers un objet. Si vous passez ce pointeur à un objet par valeur, vous aurez deux pointeurs vers le même objet, comme si vous affectiez une variable contenant un pointeur vers un objet vers une autre variable.



0
votes

La réponse est que les objets ne peuvent pas être passés par valeur. Ils sont toujours passés par référence. L'opérateur 'passer par référence', '&', ne fera rien d'autre que d'envelopper la référence d'objet (c'est-à-dire un pointeur) dans un wrapper, puis de passer ce nouveau pointeur.

En ce qui concerne l'utilisation de la mémoire, il n'y a pas de réponse simple autre que les résultats de la requête de base de données semblent entraîner beaucoup de surcharge, et vous souhaitez créer votre `` empreinte '' (c'est-à-dire tous les résultats renvoyés par une base de données, ou toute variable) aussi petit que possible.


0 commentaires