Как вы отлаживаете проблемы php «Out of Memory»?

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

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

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

Добавление функции останова помогло

register_shutdown_function('shutdown'); 

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

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

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

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

 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 таким образом, чтобы дать вам другую оценку памяти в переменной, не уверен, будет ли это лучше или хуже.

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

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

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

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

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

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

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

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

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

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

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

Веб-сайт « 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); } } 

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