web-dev-qa-db-fra.com

Comment trouver quel script PHP fuit la mémoire?

Mon serveur dédié a 32 Go RAM et la mémoire monte et monte constamment et je dois la redémarrer quotidiennement maintenant. Cela me coûte de l'argent et des clients.

J'ai du mal à trouver où se trouve la fuite de mémoire. Tout ce que je peux trouver en ligne, c'est que les gens disent "Utiliser xdebug" mais je n'ai pas pu trouver de tutoriels xdebug sur la recherche de fuites de mémoire. J'ai essayé d'imprimer memory_get_usage avant et après les appels de fonction, mais est-ce la bonne façon de le faire?

J'ai BEAUCOUP de scripts php en cours d'exécution - certains provenant de visiteurs, d'autres de tâches cron - et j'ai besoin de trouver lequel d'entre eux fuit la mémoire et de le corriger le plus tôt possible, mais je ne sais même pas comment déterminer si une fonction donnée est fuite de mémoire ou non.

J'ai essayé d'imprimer memory_get_usage avant un appel de fonction et après, et cela augmente, mais si j'appelle la fonction plus d'une fois, elle ne monte plus. Quelqu'un peut-il expliquer cela et me dire comment je peux simplement et facilement savoir si une fonction PHP a une fuite de mémoire?

29
Guy

Vous pouvez faire différentes choses, mais vous devez d'abord essayer d'éviter la création de fuites de mémoire en premier lieu.

Permettez-moi de clarifier: PHP est un langage de script et il n'est pas conçu pour les scripts de longue durée, donc sa gestion de la mémoire n'est pas la meilleure sur le marché. Mais pourquoi devrait-elle l'être? Son but est de être appelé au niveau de la demande de sorte que sa portée en cours d'exécution est assez petite (pas plus de 2 à 3 secondes). Tout le reste doit être mis en arrière-plan.

Que puis-je faire contre les fuites de mémoire?

  1. Si vous êtes dans une version inférieure à 5.4, vous devez prendre soin des références de cercle, car celles-ci ne sont pas récupérées.

  2. Si vous avez besoin d'un script à exécuter en continu, vous pourriez penser à une approche différente. Essayez une implémentation while(true), mais enveloppez supervisor ( http://supervisord.org ) autour de votre script, et laissez-le être appelé après sa fin. De cette façon, vous vous assurez à 100% de ne jamais avoir de fuites de mémoire.

  3. Vous pouvez utiliser xdebug pour profiler vos scripts un par un et découvrir où beaucoup de mémoire est consommée.

  4. Vous pouvez implémenter un destructeur pour supprimer toutes vos références si la classe n'est plus nécessaire.

    public function __destruct(){
        $this->cleanup();
    }
    
    public function cleanup() {
        //cleanup everything from attributes
        foreach (get_class_vars(__CLASS__) as $clsVar => $_) {
            unset($this->$clsVar);
        }
    
        //cleanup all objects inside data array
        if (is_array($this->_data)) {
            foreach ($this->_data as $value) {
                if (is_object($value) && method_exists($value, 'cleanUp')) {
                    $value->cleanUp();
                }
            }
        }
    }
    
  5. Lisez la documentation PHP concernant la collecte des ordures http://us3.php.net/manual/en/features.gc.php

  6. Évitez les variables globales, car celles-ci ne sont jamais récupérées et doivent être unset explicitement. Si vous utilisez un Framework comme ZF ou Symfony, cela pourrait ne pas être possible, car vous briseriez les fonctionnalités si vous le faites.

Enfin et surtout, je veux souligner encore une fois , PHP n'est pas adapté aux scripts de longue durée! Si vous avez les choses à faire, qui doivent fonctionner en continu, vous ne devriez pas vous écrouler la tête avec des fuites de mémoire en PHP, mais prendre le temps d'apprendre un langage plus sophistiqué comme Java ou C #.

22
MatthiasLaug

Regardez cette extension php: https://github.com/arnaud-lb/php-memory-profiler . Vous pouvez vider des informations dans différents formats et les analyser simplement par certains outils tels que: Google Performance Tools , KCacheGrind ou QCacheGrind .

6
Slam

J'ai trouvé une méthode qui fonctionne assez bien pour moi:

  1. Installez l'extension " php-memprof ". Dans Ubuntu, vous pouvez exécuter:

    Sudo pecl install memprof

  2. Installez " google-perftools ". Encore une fois pour Ubuntu:

    Sudo apt-get install google-perftools

  3. Ajoutez ce code au début de votre script:

    if (function_exists('memprof_enable')) {
        memprof_enable();
    }
    
  4. Et cet endroit où vous étiez expexct pour trouver une fuite de mémoire:

    if (function_exists("memprof_dump_pprof"))
    {
        $time = microtime(true);
        $f = fopen("/tmp/profile_$time.heap", "w");
        memprof_dump_pprof($f);
        fclose($f);
        echo "Memory profile dumped. ";
    }
    

    Dans mon cas, c'était à l'intérieur d'un grand cycle toutes les 100 courses.

  5. Courir google-pprof comparaison de 2 vidages mémoire:

    google-pprof --web --base=/tmp/profile_17.heap /tmp/profile_18.heap
    

    Cela ouvrira une image svg comme celle-ci dans votre navigateur:

    sample from doc

    Description des numéros et des noms à l'intérieur que vous pouvez trouver dans documentation gperftools

P.S. La correction des fuites au niveau php ne vous garantira pas qu'il n'y a pas de fuites de mémoire dans l'interpréteur. Dans mon cas, je me retrouve à redémarrer sctipt sur des périodes plus longues.

5
red_led

Je ne suis pas un expert de l'utilisation de la mémoire, mais peut-être que cette méthode vous aidera à détecter les scripts problématiques:

Obtenez des informations: 1. Utilisez les fichiers journaux d'accès Apache 2. Créez votre propre fichier journal d'utilisation de la mémoire ( http://www.webhostingtalk.com/showthread.php?t=617742 )

Vérifiez l'heure à laquelle l'utilisation de la mémoire augmente et comparez-la au journal d'accès Apache.

Il vous donnera au moins des informations si l'utilisation augmente lentement et constamment ou si elle commence à un certain point.

Bonne chance!

1
Hashbrown