PHP: наличие проблем с памятью внутри цикла
У меня есть фрагмент, который напоминает следующее:
while (true) {
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
...
$myArray = [1, 2, 3];
...
sleep(1); //Sleep at the end, to save CPU.
}
Этот фрагмент должен работать как служба демона, но у меня много проблем с этой работой.
Проблема: каждая итерация увеличивает использование памяти процесса. Как будто на каждой новой итерации создается экземпляр $myObject
, но предыдущий остается в памяти и т.д.
Я пробовал:
- to
unset
все переменные в конце цикла (прямо перед sleep()
).
- Установка всех переменных в
null
.
- инкапсулируя их в отдельную функцию (
while (true) { doThis(); }
)
- вызов вручную
gc_collect_cycles()
Ни один из них не работал для уменьшения использования памяти.
Я не знаю, как заставить всю память освободиться.
Ответы
Ответ 1
После долгих исследований по этой теме я, наконец, убедился, что нет способов вручную принудительно освободить память или заставить уничтожить объект.
Однако, что-то, что помогло мне снизить использование памяти (абсолютно предотвращение стекивания бесконечной памяти, было невозможно), заключалось в том, чтобы понять, что в PHP есть не петлевые области и что сборка мусора происходит при переключении прицелы.
В С# или Java переменная, созданная в while (...) {}
, доступна только из цикла. Это не норма для PHP. A $myObject
, созданный из инструкции while
, доступен во всем вашем приложении.
Это означает, что предоставленный фрагмент будет лучше представлен как:
while (true) {
myFunc();
}
function myFunc()
{
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
...
$myArray = [1, 2, 3];
...
sleep(1); //Sleep at the end, to save CPU.
}
Инкапсуляция логики в функции заставляет область изменения, что означает, что на каждой итерации будет вызываться сборщик мусора. У этого не решена моя проблема, но она несколько снизила использование моей памяти.
Что я узнал из этого опыта, так это то, что PHP, вероятно, не подходит для этого конкретного требования к проекту. Мне нужно больше контролировать память, а PHP не обеспечивает никакого контроля над созданными/уничтоженными объектами. Некоторые встроенные функции не освобождают память должным образом (особенно те, которые делают I/O, доступ к базе данных и memcached).
Ответ 2
Я комментирую мои предыдущие комментарии в ответе здесь. Это не объясняет, как вы можете освободить выделенную память, но проведет вас через способ узнать, что в вашем приложении вызывает это. Благодаря этому вы сможете оптимизировать свой код.
Поиск узких мест использования памяти обычно является сложной задачей. Вы можете начать с рассмотрения ваших вызовов, связанных с I/O, таких как запросы к базе данных, доступ к файлам или даже сетевое взаимодействие. Помимо увеличения времени выполнения, иногда эти операции могут выделять некоторый объем памяти.
Если вы уже освобождаете память из ресурсов, возвращаемых операциями ввода-вывода, и заметного уменьшения в выделенной памяти не наблюдается, следующим шагом может быть профилирование вашего приложения с помощью инструмента, такого как Blackfire (https://blackfire.io/).
Blackfire предоставит вам подробный обзор каждого вызова функции и ее статистики по памяти, процессору и времени выполнения. С помощью этих данных можно проверить, какие операции распределяют избыточную память. Вы можете найти эту информацию, когда вы наводите указатель мыши на панель памяти внутри деталей вызова, например: http://i.imgur.com/762mWmF. PNG
Ответ 3
Очень вероятно (с предоставленной информацией), что все еще есть ссылки на созданный объект, который предотвращает сборщик мусора из удаление объекта из памяти. Поскольку он в основном подсчитывает ссылки, поэтому, чтобы гарантировать, что никакая ссылка не будет сохранена, либо путем создания копий значений, либо с осторожностью их устранения, этого можно избежать.
Обычно при использовании конструкций while (true) проще создавать объекты именно по этой причине и сделать их максимально возможными, чтобы убедиться, что утечки памяти не могут произойти.
Я знаю, что этот ответ не очень полезен в прямой форме (и у меня недостаточно комментариев, чтобы комментировать вопрос), но это может привести вас к правильному пути.
Ответ 4
Мое лучшее предположение (из-за незнания внутренних компонентов участвующих классов) заключается в том, что либо классы назначают другие объекты в качестве своих свойств (или, возможно, имеют собственные ссылки), либо используют этот массив (поскольку пример кода напоминает реальный случай) имеет ссылку на себя, что объясняет утечку памяти, если размер массива значителен.
Если это поможет, ознакомьтесь с основными принципами подсчета ссылок из php.net:
http://php.net/manual/en/features.gc.refcounting-basics.php
Ответ 5
Как упоминалось выше как @m1lt0n и @MartPluijmaekers, это может быть проблема, связанная с ссылками на объекты.
Мы не знаем, что находится внутри вашего метода Class()
и getSomeOtherObj()
, поэтому я не могу сказать ничего точно, однако ниже фрагмент может помочь вам разобраться, если это так или нет.
/* Here is your Class */
class Class {
public function __construct () {
$this->child = new AnotherClass( $this );
}
/* This is what you have to have ... */
protected function __destruct () {
unset( $this->child );
}
}
/* Here is the other class */
class AnotherClass {
public function __construct ( $parent ) {
$this->parent = $parent;
}
}
/* Infinite Loop */
while ( true ) {
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
$myArray = [1, 2, 3];
/* manually destroying the object */
$myObject->__destruct();
unset( $myObject );
/* rest of free-ing goes here ... */
sleep(1); //Sleep at the end, to save CPU.
}
Отрывок должен быть в значительной степени самоочевидным.
Ответ 6
Проблема заключается в том, что вы находитесь в бесконечном цикле, без конца в запросе. Сборщик мусора PHP предназначен для обработки в конце запроса и в противном случае он недоступен для пользователя. PHP призван быть вызванным и отброшенным, а не оставаться в живых неопределенно. Следовательно, он не фиксируется. Итак, я бы предложил создать задачу chron, которая периодически перезапускает php-цикл, тем самым заканчивая запрос и освобождая память. Подробнее см. этот документ.