Comment déterminer l'empreinte mémoire (taille) d'une variable?


102

Existe-t-il une fonction en PHP (ou une extension PHP) pour savoir combien de mémoire une variable donnée utilise? sizeofme dit simplement le nombre d'éléments / propriétés.

memory_get_usageaide en ce qu'il me donne la taille de la mémoire utilisée par l' ensemble du script. Existe-t-il un moyen de le faire pour une seule variable?

Notez que c'est sur une machine de développement, donc le chargement d'extensions ou d'outils de débogage est possible.


Modifié - c'est 5 ans plus tard, et certains problèmes ne sont toujours pas résolus :(
Piskvor a quitté le bâtiment

Réponses:


46

Vous avez probablement besoin d'un profileur de mémoire. J'ai rassemblé des informations pour SO mais j'ai copié quelque chose d'important qui peut aussi vous aider.

Comme vous le savez probablement, Xdebug a abandonné le support du profilage de la mémoire depuis la version 2. *. Veuillez rechercher la chaîne "fonctions supprimées" ici: http://www.xdebug.org/updates.php

Fonctions supprimées

Suppression de la prise en charge du profilage de la mémoire car cela ne fonctionnait pas correctement.

Autres options de profileur

php-memory-profiler

https://github.com/arnaud-lb/php-memory-profiler . Voici ce que j'ai fait sur mon serveur Ubuntu pour l'activer:

sudo apt-get install libjudy-dev libjudydebian1
sudo pecl install memprof
echo "extension=memprof.so" > /etc/php5/mods-available/memprof.ini
sudo php5enmod memprof
service apache2 restart

Et puis dans mon code:

<?php
memprof_enable();
// do your stuff
memprof_dump_callgrind(fopen("/tmp/callgrind.out", "w"));

Enfin ouvrez le callgrind.outfichier avec KCachegrind

Utilisation de Google gperftools (recommandé!)

Tout d'abord, installez Google gperftools en téléchargeant le dernier package ici: https://code.google.com/p/gperftools/

Puis comme toujours:

sudo apt-get update
sudo apt-get install libunwind-dev -y
./configure
make
make install

Maintenant dans votre code:

memprof_enable();

// do your magic

memprof_dump_pprof(fopen("/tmp/profile.heap", "w"));

Ouvrez ensuite votre terminal et lancez:

pprof --web /tmp/profile.heap

pprof créera une nouvelle fenêtre dans votre session de navigateur existante avec quelque chose comme indiqué ci-dessous:

Profilage de la mémoire PHP avec memprof et gperftools

Xhprof + Xhgui (le meilleur à mon avis pour profiler à la fois le processeur et la mémoire)

Avec Xhprof et Xhgui, vous pouvez également profiler l'utilisation du processeur ou simplement l'utilisation de la mémoire si c'est votre problème pour le moment. C'est une solution très complète, elle vous donne un contrôle total et les journaux peuvent être écrits à la fois sur mongo ou dans le système de fichiers.

Pour plus de détails, cliquez ici .

Blackfire

Blackfire est un profileur PHP de SensioLabs, les gars de Symfony2 https://blackfire.io/

Si vous utilisez puphpet pour configurer votre machine virtuelle, vous serez heureux de savoir qu'elle est prise en charge ;-)

Xdebug et suivi de l'utilisation de la mémoire

XDEBUG2 est une extension pour PHP. Xdebug vous permet de consigner tous les appels de fonction, y compris les paramètres et de renvoyer les valeurs à un fichier dans différents formats. Il existe trois formats de sortie. L'une est conçue comme une trace lisible par l'homme, une autre est plus adaptée aux programmes informatiques car elle est plus facile à analyser, et la dernière utilise HTML pour formater la trace. Vous pouvez basculer entre les deux formats différents avec le paramètre. Un exemple serait disponible ici

pour P

forp profileur PHP simple, non intrusif et orienté production. Certaines fonctionnalités sont:

  • mesure du temps et de la mémoire allouée pour chaque fonction

  • l'utilisation du processeur

  • numéro de fichier et de ligne de l'appel de fonction

  • sortie au format d'événement de trace de Google

  • légende des fonctions

  • regroupement de fonctions

  • alias de fonctions (utiles pour les fonctions anonymes)

DBG

DBG est un débogueur php complet, un outil interactif qui vous aide à déboguer des scripts php. Il fonctionne sur un serveur WEB de production et / ou de développement et vous permet de déboguer vos scripts en local ou à distance, depuis un IDE ou une console et ses fonctionnalités sont:

  • Débogage distant et local

  • Activation explicite et implicite

  • Pile d'appels, y compris les appels de fonction, les appels de méthode dynamiques et statiques, avec leurs paramètres

  • Navigation dans la pile d'appels avec possibilité d'évaluer les variables aux endroits correspondants (imbriqués)

  • Step in / Step out / Step over / Run to curseur fonctionnalité

  • Points d'arrêt conditionnels

  • Points d'arrêt globaux

  • Journalisation des erreurs et des avertissements

  • Plusieurs sessions simultanées pour le débogage parallèle

  • Prise en charge des interfaces GUI et CLI

  • Réseaux IPv6 et IPv4 pris en charge

  • Toutes les données transférées par le débogueur peuvent être éventuellement protégées avec SSL


2
C'est exactement l'information que je cherchais, merci.
Piskvor a quitté le bâtiment

93

Il n'y a pas de moyen direct d'obtenir l'utilisation de la mémoire d'une seule variable, mais comme Gordon l'a suggéré, vous pouvez l'utiliser memory_get_usage. Cela renverra la quantité totale de mémoire allouée, vous pouvez donc utiliser une solution de contournement et mesurer l'utilisation avant et après pour obtenir l'utilisation d'une seule variable. C'est un peu piraté, mais cela devrait fonctionner.

$start_memory = memory_get_usage();
$foo = "Some variable";
echo memory_get_usage() - $start_memory;

Notez que ce n'est en aucun cas une méthode fiable, vous ne pouvez pas être sûr que rien d'autre ne touche la mémoire lors de l'affectation de la variable, donc cela ne doit être utilisé que comme une approximation.

Vous pouvez en fait transformer cela en fonction en créant une copie de la variable à l'intérieur de la fonction et en mesurant la mémoire utilisée. Je n'ai pas testé cela, mais en principe, je ne vois rien de mal à cela:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $tmp = unserialize(serialize($var));
    return memory_get_usage() - $start_memory;
}

14
$tmp = $varcréera une copie superficielle. Cela n'allouera pas plus de mémoire tant que $ tmp n'aura pas été modifié.
Gordon

@ Gordon, vous avez raison, j'ai un peu oublié ce point. Comme je ne peux pas trouver un moyen approprié de modifier la variable sans changer son type ou sa taille, je laisserai cela. Peut-être que quelqu'un peut avoir une bonne idée :)
Tatu Ulmanen

7
que diriez-vous $tmp = unserialize(serialize($var)); Cela combinerait l'approche d'Aistina ci-dessus.
Gordon

3
De plus, comme il $vars'agit déjà d'une copie superficielle ou d'une référence de ce qui a été passé à la fonction, vous n'en avez pas besoin $tmp, mais vous pouvez le réaffecter $var. Cela enregistre la référence interne de $tmpà $var.
Gordon

N'y at - il une façon plus élégante de déréférencer à $tmppartir $var?
Tomáš Zato - Réintégrer Monica

24

Non, il n'y en a pas. Mais vous pouvez serialize($var)et vérifiez strlenle résultat pour une approximation.


C'est une bien meilleure approche, car elle évite toute la chose GC.
Gleno

12
C'est une terrible approximation. Chaque élément d'un tableau en PHP fait ~ 80 octets, mais en strlen(serialize(array(1,2,3)))vaut 30.
gsnedders

2
@Aistina, -1. vous mesurez la mauvaise chose. La variable et la variable sérialisée sont deux choses totalement différentes et donneront des résultats complètement différents.
Pacerier

1
Non seulement cela, mais cela échouera complètement sur certaines structures de données non sérialisables, par exemple les références circulaires.
duskwuff -inactive-

20

En réponse à Tatu Ulmanens, répondez:

Il convient de noter que $start_memorylui - même prendra de la mémoire ( PHP_INT_SIZE * 8).

Ainsi, toute la fonction devrait devenir:

function sizeofvar($var) {
    $start_memory = memory_get_usage();
    $var = unserialize(serialize($var));
    return memory_get_usage() - $start_memory - PHP_INT_SIZE * 8;
}

Désolé d'ajouter ceci comme réponse supplémentaire, mais je ne peux pas encore commenter une réponse.

Mise à jour: le * 8 n'est pas définitif. Cela peut dépendre apparemment de la version php et éventuellement du 64/32 bit.


4
Pouvez-vous expliquer pourquoi * 8? Merci!
sierrasdetandil

@sierrasdetandil Il semble que $ start_memory ne prend pas que des PHP_INT_SIZEoctets, mais PHP_INT_SIZE*8. Vous pouvez essayer cela en appelant cette fonction, elle devrait renvoyer 0:function sizeofvar() { $start_memory = memory_get_usage(); return memory_get_usage() - $start_memory - PHP_INT_SIZE*8; }
para

8ne semble pas constant. Suite à votre fonction de commentaire sur mon système de développement (PHP 5.6.19), il revient -16. De plus, il est intéressant de noter que depuis php -a, l'appel des deux lignes de la fonction donne différentes valeurs.
Paul DelRe

@PaulDelRe oui, cela dépend probablement de la version / 64bit de ce genre de choses.
pour le

maintenant, l'erreur fatale se produit lors de l'appel unserialize (). Cela n'aide pas! Si une variable est si grande qu'elle manque de mémoire, l'appel d'une fonction sur cette variable utilisera encore PLUS de mémoire. :(
john ktejik le

4

Voir:

Notez que cela ne vous donnera pas l'utilisation de la mémoire d'une variable spécifique. Mais vous pouvez appeler ces fonctions avant et après l'affectation de la variable, puis comparer les valeurs. Cela devrait vous donner une idée de la mémoire utilisée.

Vous pouvez également jeter un œil à l' extension PECL Memtrack , bien que la documentation manque un peu, voire pratiquement inexistante.


Oui. Il peut être utilisé indirectement pour répondre à la question.
Notinlist

3

Vous pouvez opter pour le calcul de la différence de mémoire sur une valeur de retour de rappel. C'est une solution plus élégante disponible en PHP 5.3+.

function calculateFootprint($callback) {
    $startMemory = memory_get_usage();
    $result = call_user_func($callback);
    return memory_get_usage() - $startMemory;
}

$memoryFootprint = calculateFootprint(
    function() {
        return range(1, 1000000);
    }
);

echo ($memoryFootprint / (1024 * 1024)) . ' MB' . PHP_EOL;

3

Vous ne pouvez pas calculer rétrospectivement l'empreinte exacte d'une variable car deux variables peuvent partager le même espace alloué dans la mémoire

Essayons de partager la mémoire entre deux tableaux, nous voyons que l'allocation du deuxième tableau coûte la moitié de la mémoire du premier. Lorsque nous désactivons le premier, presque toute la mémoire est encore utilisée par le second.

echo memory_get_usage()."\n"; // <-- 433200
$c=range(1,100);
echo memory_get_usage()."\n"; // <-- 444348 (+11148)
$d=array_slice($c, 1);
echo memory_get_usage()."\n"; // <-- 451040 (+6692)
unset($c);
echo memory_get_usage()."\n"; // <-- 444232 (-6808)
unset($d);
echo memory_get_usage()."\n"; // <-- 433200 (-11032)

Nous ne pouvons donc pas conclure que le deuxième tableau utilise la moitié de la mémoire, car il devient faux lorsque nous annulons le premier.

Pour une vue complète de la façon dont la mémoire est allouée en PHP et pour quelle utilisation, je vous suggère de lire l'article suivant: Quelle est la taille réelle des tableaux (et des valeurs) PHP? (Indice: GRAND!)

Les principes de base du comptage de références dans la documentation PHP contiennent également de nombreuses informations sur l'utilisation de la mémoire et les références comptent pour un segment de données partagé.

Les différentes solutions exposées ici sont bonnes pour les approximations mais aucune ne peut gérer la gestion subtile de la mémoire PHP.

  1. calcul de l'espace nouvellement alloué

Si vous voulez l'espace nouvellement alloué après une affectation, vous devez l'utiliser memory_get_usage()avant et après l'allocation, car l'utiliser avec une copie vous donne une vision erronée de la réalité.

// open output buffer
echo "Result: ";
// call every function once
range(1,1); memory_get_usage();

echo memory_get_usage()."\n";
$c=range(1,100);
echo memory_get_usage()."\n";

N'oubliez pas que si vous voulez stocker le résultat de la première memory_get_usage(), la variable doit déjà exister avant, et memory_get_usage()doit être appelée une autre fois auparavant, et toutes les autres fonctions également.

Si vous voulez faire un écho comme dans l'exemple ci-dessus, votre tampon de sortie doit déjà être ouvert pour éviter que la mémoire comptable ne soit nécessaire pour ouvrir le tampon de sortie.

  1. calcul de l'espace requis

Si vous souhaitez vous fier à une fonction pour calculer l'espace requis pour stocker une copie d'une variable, le code suivant prend en charge différentes optimisations:

<?php
function getMemorySize($value) {
    // existing variable with integer value so that the next line
    // does not add memory consumption when initiating $start variable
    $start=1;
    $start=memory_get_usage();
    // json functions return less bytes consumptions than serialize
    $tmp=json_decode(json_encode($value));
    return memory_get_usage() - $start;
}

// open the output buffer, and calls the function one first time
echo ".\n";
getMemorySize(NULL);

// test inside a function in order to not care about memory used
// by the addition of the variable name to the $_GLOBAL array
function test() {
    // call the function name once 
    range(1,1);

    // we will compare the two values (see comment above about initialization of $start)
    $start=1;
    $start=memory_get_usage();
    $c=range(1,100);
    echo memory_get_usage()-$start."\n";
    echo getMemorySize($c)."\n";
}
test();

// same result, this works fine.
// 11044
// 11044

Notez que la taille du nom de la variable compte dans la mémoire allouée.

  1. Vérifiez votre code !!

Une variable a une taille de base définie par la structure C interne utilisée dans le code source PHP. Cette taille ne fluctue pas dans le cas des nombres. Pour les chaînes, cela ajouterait la longueur de la chaîne.

typedef union _zvalue_value {
    long lval;                  /* long value */
    double dval;                /* double value */
    struct {
        char *val;
        int len;
    } str;
    HashTable *ht;              /* hash table value */
    zend_object_value obj;
} zvalue_value;

Si on ne prend pas en compte l'initialisation du nom de la variable, on sait déjà combien une variable utilise (en cas de nombres et de chaînes):

44 octets dans le cas de nombres

+ 24 octets dans le cas des chaînes

+ la longueur de la chaîne (y compris le caractère NUL final)

(ces chiffres peuvent changer en fonction de la version PHP)

Vous devez arrondir à un multiple de 4 octets en raison de l'alignement de la mémoire. Si la variable est dans l'espace global (pas à l'intérieur d'une fonction), elle allouera également 64 octets supplémentaires.

Donc, si vous souhaitez utiliser l'un des codes de cette page, vous devez vérifier que le résultat à l'aide de quelques cas de test simples (chaînes ou nombres) correspond à ces données en tenant compte de chacune des indications de ce post (tableau $ _GLOBAL, premier appel de fonction, tampon de sortie, ...)


1
... et c'est même avant d'entrer dans les rouages ​​de zvalue, is_refet de copier sur écriture. Je vous remercie.
Piskvor a quitté le bâtiment

1
Grâce à vous, j'ai manqué cette page du manuel PHP. J'ai ajouté le lien pour compléter ma réponse (mais je suppose que vous avez déjà lu cela).
Adam

2

J'ai eu un problème similaire, et la solution que j'ai utilisée était d'écrire la variable dans un fichier puis d'exécuter filesize () dessus. À peu près comme ceci (code non testé):

function getVariableSize ( $foo ) 
{
    $tmpfile = "temp-" . microtime(true) . ".txt";
    file_put_contents($tmpfile, $foo);
    $size = filesize($tmpfile);
    unlink($tmpfile);
    return $size;
}

Cette solution n'est pas très rapide car elle implique des E / S de disque, mais elle devrait vous donner quelque chose de beaucoup plus exact que les astuces memory_get_usage. Cela dépend simplement de la précision dont vous avez besoin.


Il convient de noter que cette solution ne fonctionne que pour des chaînes et un tableau de chaînes à dimension unique et que son utilisation strlenserait plus facile.
Adam


1
function mesure($var){
    $start = memory_get_usage();
    if(is_string($var)){
        $newValue = $var . '';
    }elseif(is_numeric($var)){
        $newValue = $var + 0;
    }elseif(is_object($var)){
        $newValue = clone $var;
    }elseif(is_array($var)){
        $newValue = array_flip($var, []);
    }
    return memory_get_usage() - $start;
}

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.