У меня есть фрагмент, который похож на следующее:
while (true) {
$myObject = new Class();
$myOtherObject = $myObject->getSomeOtherObj();
...
$myArray = [1, 2, 3];
...
sleep(1); //Sleep at the end, to save CPU.
}
Этот фрагмент должен работать как служба демона, но у меня много проблем с выполнением этой работы.
Проблема: каждая итерация увеличивает использование памяти процессом. Как будто на каждой новой итерации новая $myObject
создается, но предыдущий остается в памяти, и тому подобное.
Я пытался:
unset
все переменные в конце цикла (прямо перед sleep()
). null
, while (true) { doThis(); }
)gc_collect_cycles()
Ни один из них не работал для уменьшения использования памяти.
Я понятия не имею, как заставить всю память быть освобожденной.
Я собираю мои предыдущие комментарии в ответе здесь. Это не объясняет, как именно вы можете освободить выделенную память, но поможет вам узнать, что в вашем приложении вызывает это. С этим вы можете работать над оптимизацией вашего кода.
Поиск узких мест в использовании памяти обычно является сложной задачей. Вы можете начать с просмотра вызовов, связанных с вводом / выводом, таких как запросы к базе данных, доступ к файлам или даже работа в сети. Помимо увеличения времени выполнения, иногда эти операции могут выделять некоторый объем памяти.
Если вы уже очищаете память от ресурсов, возвращаемых операциями ввода-вывода, и заметного уменьшения выделенной памяти не наблюдается, следующим шагом может быть профилирование вашего приложения с помощью инструмента, такого как Blackfire (https://blackfire.io/).
Blackfire предоставит вам подробный обзор каждого вызова функции и его статистику по памяти, процессору и времени выполнения. С помощью этих данных можно проверить, какие операции выделяют избыточную память. Вы можете найти эту информацию, когда наведете указатель мыши на панель памяти внутри деталей вызова, например: http://i.imgur.com/762mWmF.png
После долгих исследований по этой теме я наконец-то убедился, что нет способов принудительно освободить память вручную или вызвать разрушение объекта.
Тем не менее, кое-что, что помогло мне уменьшить использование памяти (абсолютно предотвратить бесконечную укладку памяти было невозможно), заключалось в том, что без рамок в PHP и что сборка мусора происходит при переключении областей.
В C # или Java переменная, созданная в while (...) {}
доступен только из цикла. Это не норма для PHP. $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 не обеспечивает какого-либо контроля над созданными / уничтоженными объектами. Некоторые собственные функции не освобождают память должным образом (особенно те, которые выполняют ввод / вывод, доступ к базе данных и memcached).
Весьма вероятно (при наличии предоставленной информации), что все еще есть ссылки на созданный объект, которые мешают сборщику мусора удаление объекта из памяти. Поскольку это в основном подсчет ссылок, поэтому следует убедиться, что ссылки не сохраняются, путем копирования копий значений или их аккуратного сброса — этого можно избежать.
Обычно при использовании конструкций while (true) проще не создавать объекты именно по этой причине и сделать их настолько автономными, насколько это возможно, просто чтобы убедиться, что утечки памяти на самом деле не произойдут.
Я знаю, что этот ответ не очень полезен в прямой форме (и у меня нет достаточно представителей, чтобы прокомментировать вопрос), но он может привести вас на правильный путь.
Мое лучшее предположение (из-за нехватки знаний внутренних элементов участвующих Классов) состоит в том, что либо классы назначают другие Объекты в качестве своих свойств (или, возможно, они имеют собственные ссылки), либо что используемый массив (так как образец кода напоминает реальный) case) имеет ссылку на себя, что объясняет утечку памяти, если размер массива значительный.
Если это поможет, пожалуйста, ознакомьтесь с основами подсчета ссылок на php.net:
Поскольку и @ 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.
}
Фрагмент должен быть в значительной степени самоочевидным.
Проблема в том, что вы находитесь в бесконечном цикле без конца в запросе. Сборщик мусора в PHP предназначен для обработки в конце запроса, и в противном случае он недоступен для пользователя. PHP предназначен для вызова и отбрасывания, а не для поддержки в течение неопределенного времени. Следовательно это не поправимо. Итак, я бы предложил создать работу chron, которая через равные промежутки времени перезапускает цикл php, завершая таким образом запрос и освобождая память. Увидеть этот документ для деталей.