У меня есть процесс, использующий много памяти, и этот процесс выполняется внутри al oop, который может иметь что-то вроде 100 итераций. Для каждой итерации процесс потребляет ~ 3 МБ, и сборщик мусора php не может освободить эту память между каждой итерацией.
Сначала я попытался освободить эту утечку памяти (сбросив некоторые выбранные переменных или установив их на null
), но напрасно.
Итак, в качестве обходного пути Я бы хотел, чтобы процесс завершился изящно до сбоя в Чтобы сохранить базу данных в чистоте. Поэтому я стараюсь предсказать, как можно точнее, когда пора останавливаться, чтобы предотвратить появление памяти cra sh.
. Для этого я использовал memory_get_usage()
и начал отлаживать происходящее. С этим методом я наконец-то увидел, что после каждой итерации около 3 Мбайт памяти протекало. Но когда в памяти происходит cra sh, остается еще около 37 МБ памяти, что, по-видимому, указывает на то, что внутреннему процессу требуется такой объем памяти для работы (~ 37 МБ выделенной памяти, затем только ~ 34 МБ освобождено).
Учитывая, что первые итерации всегда будут работать, как я могу более точно предсказать, сколько реальной памяти необходимо для выполнения одного шага? Я надеялся, что memory_get_peak_usage()
даст правильную информацию (например, memory_get_usage()
+ ~ 37Mb), но странно, возвращаемое значение всегда ниже, чем memory_get_usage()
.
РЕДАКТИРОВАТЬ: чтобы объяснить это по-другому, моя цель чтобы остановить выполнение до , предел памяти достигнут. Но ограничение фиксированным числом итераций не очень точно: мне нужно догадаться (приблизительно), когда память достигнет критического объема, затем аккуратно остановить выполнение.
$memoryAnalysis = new MemoryAnalysis();
echo "\n";
echo "####### INITIAL MEMORY: ".$memoryAnalysis->getMemoryUsage(true)."\n";
echo "####### INITIAL MEMORY PEAK: ".$memoryAnalysis->getMemoryPeakUsage(true)."\n";
echo "\n";
foreach ($mails as $n => $mail) {
// ... PERFORM HUGE PROCESS ...
$memoryAnalysis->newStep();
echo "\n";
echo "####### LOOP $n\n";
echo "####### Memory: ".$memoryAnalysis->getMemoryUsage(true)."\n";
echo "####### Memory peak: ".$memoryAnalysis->getMemoryPeakUsage(true)."\n";
echo "####### Average memory per step: ".$memoryAnalysis->getMemoryUsagePerStep(true)."\n";
echo "####### Average memory peak per step: ".$memoryAnalysis->getMemoryPeakUsagePerStep(true)."\n";
}
Вот код для MemoryAnalysis
класса:
class MemoryAnalysis
{
/**
* @var int
*/
protected $nbSteps;
/**
* @var int
*/
protected $initialMemoryUsage;
/**
* @var int
*/
protected $initialMemoryPeakUsage;
/**
* MemoryAnalysis constructor.
*/
public function __construct()
{
$this->nbSteps = 0;
$this->initialMemoryUsage = memory_get_usage();
$this->initialMemoryPeakUsage = memory_get_peak_usage();
}
/**
* Add a new step after a huge memory use has been done.
*
* @return void
*/
public function newStep(): void
{
$this->nbSteps++;
}
/**
* Return the amount of used memory since the creation of the objecT.
*
* @param bool $formatted Return a formatted string instead of an integer
*
* @return int|string
*/
public function getMemoryUsage(bool $formatted = false)
{
$mem = memory_get_usage() - $this->initialMemoryUsage;
return $formatted ? self::format($mem) : $mem;
}
/**
* Return the average memory usage per step.
*
* @param bool $formatted Return a formatted string instead of an integer
*
* @return int|string|null Null if no step was added
*/
public function getMemoryUsagePerStep(bool $formatted = false)
{
if ($this->nbSteps < 0) {
return null;
}
$mem = $this->getMemoryUsage() / $this->nbSteps;
return $formatted ? self::format($mem) : $mem;
}
/**
* Return the amount of used memory since the creation of the objecT.
*
* @param bool $formatted Return a formatted string instead of an integer
*
* @return int|string
*/
public function getMemoryPeakUsage(bool $formatted = false)
{
$mem = memory_get_peak_usage() - $this->initialMemoryPeakUsage;
return $formatted ? self::format($mem) : $mem;
}
/**
* Return the average memory usage per step.
*
* @param bool $formatted Return a formatted string instead of an integer
*
* @return int|string|null Null if no step was added
*/
public function getMemoryPeakUsagePerStep(bool $formatted = false)
{
if ($this->nbSteps === 0) {
return null;
}
$mem = $this->getMemoryPeakUsage() / $this->nbSteps;
return $formatted ? self::format($mem) : $mem;
}
/**
* Format a long number in a readable way.
*
* @param int $n Number
*
* @return string String separated by commas
*/
public static function format(int $n): string
{
$n = (string) $n;
$len = strlen($n);
$nbGroups = (int) floor(($len - 1) / 3);
$output = '';
for ($i = 0; $i !== $nbGroups + 1; $i++) {
$start = $len - ($i + 1) * 3;
$groupLen = min(3, $start + 3);
$start = max(0, $start);
$group = substr($n, $start, $groupLen);
$output = ',' . $group . $output;
}
return substr($output, 1);
}
}
И вот вывод, который я получаю:
####### INITIAL MEMORY: 408
####### INITIAL MEMORY PEAK: 0
####### LOOP 0
####### Memory: 15,222,912
####### Memory peak: 15,347,416
####### Average memory per step: 15,222,912
####### Average memory peak per step: 15,347,416
####### LOOP 1
####### Memory: 18,449,320
####### Memory peak: 18,506,312
####### Average memory per step: 9,224,660
####### Average memory peak per step: 9,253,156
####### LOOP 2
####### Memory: 21,043,056
####### Memory peak: 21,073,976
####### Average memory per step: 7,014,352
####### Average memory peak per step: 7,024,658
####### LOOP 3
####### Memory: 23,620,488
####### Memory peak: 23,629,968
####### Average memory per step: 5,905,122
####### Average memory peak per step: 5,907,492
[...]
####### LOOP 30
####### Memory: 89,779,456
####### Memory peak: 89,837,232
####### Average memory per step: 2,896,111
####### Average memory peak per step: 2,897,975
####### LOOP 31
####### Memory: 97,670,280
####### Memory peak: 97,713,624
####### Average memory per step: 3,052,196
####### Average memory peak per step: 3,053,550
Здесь l oop ломается и показывает следующую ошибку:
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 20480 bytes) in /var/www/project/vendor/symfony/options-resolver/OptionsResolver.php on line 805
Fatal error: Allowed memory size of 134217728 bytes exhausted (tried to allocate 32768 bytes) in /var/www/project/vendor/symfony/debug/Exception/OutOfMemoryException.php on line 1
ПРИМЕЧАНИЕ : среднее потребление памяти может быть более точным, если пропустить первый шаг, который занимает действительно больше памяти, чем следующие. Но мой вопрос больше об этом «невидимом» использовании памяти размером ~ 37 МБ в примере, который я не знаю, как предсказать.
РЕДАКТИРОВАТЬ 2 : благодаря предоставленным советам по комментариям sintakonte потребление памяти было разделено на 3. Но вопрос остается открытым.