Как узнать, в каком PHP-скрипте происходит утечка памяти?


Мой выделенный сервер имеет 32 ГБ оперативной памяти, и объем памяти постоянно увеличивается, и теперь мне приходится перезагружать его ежедневно. Это стоит мне клиентов и денег.

Мне трудно найти место утечки памяти. Все, что я могу найти в Интернете, это то, что люди говорят "Используйте xdebug", но я не смог найти никаких руководств по xdebug по поиску утечек памяти. Я пробовал печатать memory_get_usage до и после вызовов функций, но правильно ли это делать?

У меня МНОГО запущены php-скрипты - некоторые от посетителей, другие от заданий cron - и мне нужно найти, у кого из них происходит утечка памяти, и исправить это как можно скорее, но я даже не знаю, как определить, является ли данная функция утечкой памяти или нет.

Я пробовал печатать memory_get_usage до вызова функции и после, и он увеличивается, но затем, если я вызываю функцию более одного раза, он больше не увеличивается. Может кто-нибудь, пожалуйста, объяснить это и сказать мне, как я могу просто и легко определить, имеет ли функция PHP утечка памяти?

Author: Guy, 2013-04-17

4 answers

Вы могли бы делать разные вещи, но сначала вы должны попытаться избежать создания утечек памяти в первую очередь.

Позвольте мне уточнить: PHP - это язык сценариев, и он не предназначен для длительных сценариев, поэтому его управление памятью не является лучшим на рынке. Но почему это должно быть так? Его цель состоит в том, чтобы вызываться на уровне запроса, поэтому область его выполнения довольно мала (не более 2-3 секунд). Все остальное должно быть отодвинуто на задний план.

Что я могу сделать против утечек памяти?

  1. Если у вас версия ниже 5.4, вам необходимо позаботиться о ссылках на круги, так как они не являются собранным мусором.

  2. Если вам нужен сценарий, который будет выполняться непрерывно, вы можете подумать о другом подходе. Попробуйте реализацию while(true), но оберните supervisor (http://supervisord.org ) вокруг вашего скрипта, и пусть он будет вызван после его завершения. Таким образом, вы на 100 % уверены, что у вас никогда не будет утечек памяти.

  3. Ты можно было бы использовать xdebug для профилирования ваших сценариев один за другим и выяснить, где потребляется много памяти.

  4. Вы можете реализовать деструктор, чтобы отменить все ваши ссылки, если класс больше не нужен.

    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. Прочитайте документацию PHP, касающуюся сборки мусора http://us3.php.net/manual/en/features.gc.php

  6. Избегайте глобальных переменных, потому что они никогда не собираются как мусор и должны быть unset явно. Если вы используете фреймворк, такой как ZF или Symfony, это может быть невозможно, так как вы нарушите функциональность, если сделаете это.

И последнее, но не менее важное, я хочу еще раз подчеркнуть, PHP не подходит для длительных сценариев! Если у вас есть дела, которые нужно выполнять непрерывно, вам не следует ломать голову из-за утечек памяти в PHP, а потратьте время на изучение более сложного языка, такого как JAVA или C#.

 19
Author: MatthiasLaug, 2013-04-26 17:26:28

Посмотрите на это php-расширение: https://github.com/arnaud-lb/php-memory-profiler . Вы сможете сбрасывать информацию в различных форматах и просто анализировать ее с помощью некоторых инструментов, таких как: Инструменты производительности Google, KCachegrind или qcachegrind.

 6
Author: Slam, 2014-07-01 18:52:26

Я нашел метод, который довольно хорошо работает для меня:

  1. Установите расширение "php-memprof". В Ubuntu вы можете запустить:

    sudo pecl install memprof

  2. Установите "google-perftools". Снова для Ubuntu:

    sudo apt-get install google-perftools

  3. Добавьте этот код в начало вашего скрипта:

    if (function_exists('memprof_enable')) {
        memprof_enable();
    }
    
  4. И в этом месте вы ожидали обнаружить утечку памяти:

    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. ";
    }
    

    В моем случае это было внутри большого цикла каждые 100 бежит.

  5. Запустите google-pprof сравнение 2 дампов памяти:

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

    Это откроет в вашем браузере изображение svg следующим образом:

    sample from doc

    Описание номеров и имен внутри вы можете найти в документации gperftools

P.S. Исправление утечек на уровне php не гарантирует вам, что в интерпретаторе нет утечек памяти. В моем случае я заканчиваю просто перезапуском sctipt в более длительные периоды.

 5
Author: red_led, 2017-03-13 05:24:47

Я не эксперт по использованию памяти, но, возможно, этот метод поможет вам обнаружить проблемные сценарии:

Получить информацию: 1. Используйте файлы журнала доступа apache 2. Создайте свой собственный файл журнала использования памяти (http://www.webhostingtalk.com/showthread.php?t=617742)

Проверьте время увеличения использования памяти и сравните с журналом доступа apache.

Это, по крайней мере, даст вам информацию о том, растет ли использование медленно и постоянно или оно начинается с определенный момент.

Удачи!

 1
Author: Hashbrown, 2013-05-03 14:33:11