Как вы отлаживаете проблемы 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?
Ответы
Ответ 1
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
таким образом, чтобы дать вам другую оценку памяти в переменной, не уверен, будет ли это лучше или хуже.
Ответ 2
Memprof - это расширение php, которое помогает находить эти фрагменты памяти, особенно в объектно-ориентированных кодах.
Этот адаптированный учебник весьма полезен.
Примечание. Я безуспешно пытался скомпилировать это расширение для окон. Если вы попытаетесь сделать это, убедитесь, что ваш php не является потокобезопасным. Чтобы избежать некоторых головных болей, я предлагаю вам использовать его в средах * nix.
Еще одна интересная ссылка была slideshare, описывающая, как php обрабатывает память. Это дает вам некоторые подсказки в отношении использования памяти script.
Ответ 3
Интересно, возможно, ваше мышление считает, что методология здесь испорчена.
Основной ответ на ваш вопрос - как узнать, где происходит эта ошибка? - уже был дан ответ; вы знаете, что это значит.
Однако это один из тех случаев, когда ошибка запуска не является проблемой - конечно, этот 232-байтовый объект не является вашей проблемой вообще. Это 20 + Megs, которые были выделены до него.
Было опубликовано несколько идей, которые помогут вам отслеживать это; вам действительно нужно посмотреть "более высокий уровень" здесь, в архитектуре приложения, а не только на отдельные функции.
Возможно, ваше приложение требует больше памяти, чтобы делать то, что он делает, с пользовательской загрузкой. Или может случиться так, что есть какие-то настоящие боты памяти, которые не нужны, - но вы должны знать, что необходимо или не отвечать на этот вопрос.
В основном это означает, что нужно по очереди, по-объекту, профилировать по мере необходимости, пока не найдете то, что ищете; пользователи большой памяти. Обратите внимание, что не может быть одного или двух больших предметов... если бы это было так просто! Как только вы найдете память-свиньи, вам нужно выяснить, могут ли они быть оптимизированы. Если нет, вам потребуется больше памяти.
Ответ 4
Проверьте документацию функции memory_get_usage(), чтобы просмотреть использование памяти во время выполнения.
Ответ 5
Веб-сайт 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);
}
}
Ответ 6
Используйте xdebug для использования памяти в профиле.