Как вы решаете проблемы с php "Out of Memory"? - PullRequest
29 голосов
/ 24 мая 2011

В последнее время у меня были некоторые проблемы с ограничением памяти PHP:

Недостаточно памяти (выделено 22544384) (попытался выделить 232 байта)

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

Помогло добавление функции выключения

register_shutdown_function('shutdown');

затем, используя error_get_last (); Я могу получить информацию о последней ошибке, в данном случае о фатальной ошибке «Недостаточно памяти», такой как номер строки и имя файла php.

Это хорошо и все, но моя php-программа сильно ориентирована на объекты. Ошибка глубоко в стеке не говорит мне много о структуре управления или стеке выполнения в момент ошибки. Я попытался debug_backtrace (), , но это просто показывает мне стек во время выключения, а не стек во время ошибки.

Я знаю, что могу просто увеличить предел памяти, используя ini_set или модифицируя php.ini, но это не приближает меня к фактическому выяснению того, что потребляет столько памяти или как выглядит мой поток выполнения во время ошибки.

У кого-нибудь есть хорошая методика отладки ошибок памяти в продвинутых объектно-ориентированных PHP-программах?

Ответы [ 6 ]

11 голосов
/ 27 марта 2013
echo '<pre>';
$vars = get_defined_vars();
foreach($vars as $name=>$var)
{
    echo '<strong>' . $name . '</strong>: ' . strlen(serialize($var)) . '<br />';
}
exit();

/* ... Code that triggers memory error ... */

Я использую это, чтобы распечатать список назначенных в данный момент переменных непосредственно перед проблемным разделом моего кода, вместе с (очень) грубой оценкой размера переменной.Я возвращаюсь и unset все, что не нужно в точке интереса и за ее пределами.

Это полезно, когда установка расширения не вариант.

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

7 голосов
/ 21 ноября 2013

Memprof - расширение php, которое помогает находить фрагменты тех, кто использует память, особенно в объектно-ориентированных кодах.

Этот адаптированный учебник весьма полезен.

Примечание. Я безуспешно пытался скомпилировать это расширение для Windows. Если вы попробуете так, убедитесь, что ваш php не является потокобезопасным. Чтобы избежать головной боли, я предлагаю вам использовать его в * nix средах.

Другой интересной ссылкой был slideshare , описывающий, как php обрабатывает память. Это дает вам некоторые подсказки об использовании памяти вашего скрипта.

6 голосов
/ 24 мая 2011

Интересно, может быть, ваша методология с учетом вашего мышления здесь ошибочна?

Основной ответ на ваш вопрос - как мне узнать, где происходит эта ошибка?- уже был дан ответ;Вы знаете, что является причиной этого.

Однако это один из тех случаев, когда ошибка запуска не является проблемой - конечно, этот 232-байтный объект вовсе не является вашей проблемой.Перед ним было выделено 20+ мег.

Были опубликованы некоторые идеи, которые могут помочь вам отследить это;вам действительно нужно смотреть на «более высокий уровень» здесь, на архитектуру приложения, а не только на отдельные функции.

Возможно, вашему приложению требуется больше памяти, чтобы делать то, что оно делает, с учетом нагрузки пользователя,Или, может быть, есть настоящие свиньи в памяти, которые не нужны - но вы должны знать , что необходимо, или нет, чтобы ответить на этот вопрос.линия, объект за объектом, профилирование по мере необходимости, пока вы не найдете то, что ищете;пользователи большой памяти.Обратите внимание, что не может быть одного или двух больших предметов ... если бы это было так просто!После того, как вы обнаружите боровов памяти, вам придется выяснить, можно ли их оптимизировать.Если нет, то вам нужно больше памяти.

3 голосов
/ 27 марта 2013

Веб-сайт " IF! 1 0 " предоставляет простой в использовании MemoryUsageInformation класс.Это очень полезно для устранения утечек памяти.

<?php

class MemoryUsageInformation
{

    private $real_usage;
    private $statistics = array();

    // Memory Usage Information constructor
    public function __construct($real_usage = false)
    {
        $this->real_usage = $real_usage;
    }

    // Returns current memory usage with or without styling
    public function getCurrentMemoryUsage($with_style = true)
    {
        $mem = memory_get_usage($this->real_usage);
        return ($with_style) ? $this->byteFormat($mem) : $mem;
    }

    // Returns peak of memory usage
    public function getPeakMemoryUsage($with_style = true)
    {
        $mem = memory_get_peak_usage($this->real_usage);
        return ($with_style) ? $this->byteFormat($mem) : $mem;
    }

    // Set memory usage with info
    public function setMemoryUsage($info = '')
    {
        $this->statistics[] = array('time' => time(),
            'info' => $info,
            'memory_usage' => $this->getCurrentMemoryUsage());
    }

    // Print all memory usage info and memory limit and 
    public function printMemoryUsageInformation()
    {
        foreach ($this->statistics as $satistic)
        {
            echo "Time: " . $satistic['time'] .
            " | Memory Usage: " . $satistic['memory_usage'] .
            " | Info: " . $satistic['info'];
            echo "\n";
        }
        echo "\n\n";
        echo "Peak of memory usage: " . $this->getPeakMemoryUsage();
        echo "\n\n";
    }

    // Set start with default info or some custom info
    public function setStart($info = 'Initial Memory Usage')
    {
        $this->setMemoryUsage($info);
    }

    // Set end with default info or some custom info
    public function setEnd($info = 'Memory Usage at the End')
    {
        $this->setMemoryUsage($info);
    }

    // Byte formatting
    private function byteFormat($bytes, $unit = "", $decimals = 2)
    {
        $units = array('B' => 0, 'KB' => 1, 'MB' => 2, 'GB' => 3, 'TB' => 4,
            'PB' => 5, 'EB' => 6, 'ZB' => 7, 'YB' => 8);

        $value = 0;
        if ($bytes > 0)
        {
            // Generate automatic prefix by bytes 
            // If wrong prefix given
            if (!array_key_exists($unit, $units))
            {
                $pow = floor(log($bytes) / log(1024));
                $unit = array_search($pow, $units);
            }

            // Calculate byte value by prefix
            $value = ($bytes / pow(1024, floor($units[$unit])));
        }

        // If decimals is not numeric or decimals is less than 0 
        // then set default value
        if (!is_numeric($decimals) || $decimals < 0)
        {
            $decimals = 2;
        }

        // Format output
        return sprintf('%.' . $decimals . 'f ' . $unit, $value);
    }

}
3 голосов
/ 24 мая 2011

Проверьте документацию по функции memory_get_usage () , чтобы просмотреть использование памяти во время выполнения.

0 голосов
/ 24 мая 2011

Используйте xdebug для использования памяти профиля.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...