Letysite.ru

IT Новости с интернет пространства
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Php сборщик мусора

Блог о починке примусов

вторник, 17 декабря 2019 г.

Сборка мусора в php

Недавно встретился хороший пост для начинающих о сборке мусора (он же garbage collection ) в php. Далее я попытаюсь перевести этот пост на русский и добавить немного собственных данных. Ссылка на оригинальный пост — в конце.

Начнем с того, что так как php — язык интерпретируемый, то вам не нужно заморачиваться управлением памятью — выделением памяти, и что более важно — очисткой памяти. Этим в php занимается специальный механизм, называемый сборкой мусора (или garbage collection, или же gc).

Сборка мусора работает тремя способами:

  1. При уходе переменной из области видимости
  2. При подсчете ссылок
  3. При сборе циклических ссылок

Как только переменная уходит из области видимости и больше нигде не используется — она автоматически собирается gc. Также с помощью unset можно явно определить, что переменную пора собирать gc . Пример кода:

function display_var() <
$foo = «bar»;
echo $foo;
>

$user = «Mister X»;
unset($user);

В данном коде:

  • переменная $foo будет автоматически собрана gc сразу после завершения выполнения функции display_var
  • переменная $user будет собрана gc, так как она явно удалена с помощью unset

С подсчетом ссылок разобраться чуть сложнее. Официальная часть здесь.

Кратко — для того чтобы понять, можно ли безопасно собрать переменную с помощью garbage collection, используется механизм подсчета ссылок. Данный механизм заключается в следующем — при создании переменной в php создается не просто переменная, а контейнер типа zval , в котором помимо собственно типа и значения переменной, хранится еще два поля — ref_count и is_ref . Далее, если вы присваиваете другой переменной значение этой переменной, то контейнер не копируется с имеющимися данными, а php просто увеличивает ref_count на единицу, так как контейнер используется уже двумя переменными. И так далее.

Как только в какой-то момент переменная удаляется (с помощью unset или ухода из зоны видимости), счетчик ref_count в контейнере уменьшается. Как только счетчик дошел до нуля — считается, что контейнер готов к сборке мусора.

Мониторить состояние контейнера можно при наличии расширения Xdebug с помощью функции xdebug_debug_zval .

Сборка мусора при наличии циклических ссылок не так сложна, как предыдущий пункт. В этом случае сборка мусора активируется в тот момент, когда в памяти находится 10000 объектов с циклическими ссылками, и один из них уходит из области видимости. Значение 10000 установлено на уровне ядра php и может быть изменено только путем изменения исходного кода и его перекомпиляции. Однако, процесс сборки мусора можно запустить явно, не дожидаясь накопления 10000 объектов, с помощью метода gc_collect_cycles .

Также, так как сборка мусора при наличии циклических ссылок может потребовать значительное количество ресурсов, то такую сборку можно запретить одним из двумя способов:

  • вызвать метод gc_disable
  • установить значение zend.enable_gc в false в файле php.ini ( gc_disable делает то же самое)

Также можно отметить, что изменения в php7.3 серьезно улучшили механизм сборки мусора — в оригинальном посте можно увидеть бенчмарки сравнения предыдущих релизов и версии 7.3, плюс появилась полезная функция gc_status, выводящая данные об использовании gc. А при наличии уже упомянутого Xdebug можно получить еще больше информации с помощью функции xdebug_start_gcstats .

Вот все основные моменты, что следует знать и помнить о сборке мусора. Оригинальная статья с некоторыми дополнительными плюшками — тут.

сборка мусора php при запуске скрипта

У меня есть PHP-скрипт, который работает на cron, который может занять до 15 минут. Через равные промежутки времени я выплевываю memory_get_usage (), чтобы я мог видеть, что происходит. В первый раз, когда он говорит мне, что я использую, я нахожусь в 10 мегабайтах. Когда сценарий заканчивается, я нахожусь в 114 мегабайтах!

Выполняет ли PHP сборку мусора во время работы скрипта? Или что происходит со всей этой памятью? Есть ли что-то, что я могу сделать, чтобы заставить сбор мусора. Задача, которую выполняет мой скрипт, – это ночной импорт нескольких тысяч узлов в Drupal. Так что он делает то же самое много раз.

Ключ в том, что вы отключите свои глобальные переменные, как только они вам не понадобятся.

Вам не нужно явно отключать локальные переменные и свойства объектов, поскольку они уничтожаются, когда функция выходит из области действия или объект уничтожается.

PHP сохраняет количество ссылок для всех переменных и уничтожает их (в большинстве условий), как только этот счетчик ссылок будет равен нулю. Объекты имеют один внутренний счетчик ссылок, а сами переменные (ссылки на объекты) имеют один счетчик ссылок. Когда все ссылки на объекты были уничтожены, потому что их ссылочные coutns достигли 0, сам объект будет уничтожен. Пример:

Но рассмотрим следующий сценарий:

Эти циклические ссылки – это то, где gc_collect_cycles сборщик мусора PHP 5.3. Вы можете явно вызвать сборщик мусора с помощью gc_collect_cycles .

См. Также « Основы подсчета ссылок» и « Циклы сбора» в руководстве.

Сбор мусора PHP в значительной степени является эталонным счетчиком (у него есть некоторое обнаружение цикла). Если вы сохраняете ссылки, которые все еще доступны, они легко складываются, если не освобождаются.

Читать еще:  Сервер сбора данных

Используйте unset () для освобождения переменных, которые вы больше не используете. Если вы просто перезаписываете переменные (например, с нулевым), это позволит GC уменьшить до объема пространства, требуемого этой переменной, но не так сильно, как unset, что фактически позволяет уничтожить ссылочное значение.

Вы также должны правильно выпускать любые ресурсы и т. Д., Которые вы используете.

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

Используйте unset() как можно больше, чаще проверяйте используемую память. да, php делает сборку мусора во время выполнения на нескольких условиях. здесь полезный пост на php.net.

Если память увеличивается настолько сильно, то вы, вероятно, не выпускаете ее. Вы создали утечку памяти. Сбор мусора не поможет вам, если вы не отключите переменные, не уничтожьте объекты и / или не выйдете из сферы действия.

Вы отключаете узлы, которые вы загружаете, когда закончите с ними? Я написал PHP-скрипты, которые работают часами, обрабатывая миллионы записей базы данных, без проблем и использования памяти, которые поднимаются и опускаются в очень приемлемом диапазоне.

Как работает сборщик мусора в PHP

У меня есть скрипт PHP, который имеет большой массив людей, он захватывает их данные из внешнего ресурса через SOAP, изменяет данные и отправляет их обратно. Из-за размера деталей я увеличил память PHP до 128 МБ. Примерно через 4 часа работы (вероятно, потребуется 4 дня для запуска) у него закончилась память. Вот основы того, что он делает:

После того , как он исчерпал память и разбился, я решил инициализировать $data до цикла foreach , и в соответствии с top использование памяти для процесса не поднялось выше 7.8%, и он работал в течение 12 часов.

Итак, мой вопрос: не запускает ли PHP сборщик мусора на переменных, инициализированных внутри цикла, даже если он используется повторно? Является ли система, восстанавливающая память, и PHP еще не пометил ее как полезную и в конечном итоге снова рухнет (я увеличил ее до 256 МБ, поэтому я изменил 2 вещи и не уверен, что это исправило, я мог бы, вероятно, изменить свой скрипт, чтобы ответить на это, но не хочу ждать еще 12 часов, чтобы он упал, чтобы выяснить)?

Я не использую фреймворк Zend, поэтому другой вопрос, подобный этому, я не думаю, что он уместен.

EDIT: на самом деле у меня нет проблем со сценарием или с тем, что он делает. На данный момент, что касается всей системы отчетности, у меня нет никаких проблем. Этот вопрос касается сборщика мусора и того, как / когда он восстанавливает ресурсы в цикле foreach и / или как система сообщает об использовании памяти процесса php.

2 Ответа

Я не знаю внутренностей PHP-х VM, но по моему опыту, он не собирает мусор, пока ваша страница работает. Это потому, что он выбрасывает все, что ваша страница создана, когда она заканчивается.

В большинстве случаев, когда страница исчерпывает память, а лимит довольно высок (и 128Mb не высок), возникает проблема алгоритма. Многие программисты PHP собирают структуру данных, а затем передают ее на следующий шаг, который повторяет структуру, обычно создавая другую. Вспенить, смыть, повторить. К сожалению, этот подход является большой боров памяти, и вы в конечном итоге создаете несколько копий ваших данных в памяти. Два из действительно больших изменений в PHP 5 заключались в том, что объекты подсчитываются по ссылкам, а не копируются, и вся строковая подсистема была сделана намного быстрее. Но это все еще проблема.

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

Тем не менее, вы можете сделать этот подход экономии памяти для части данных. Хитрость заключается в том, что вы явно unset() ключевая переменная или две в конце цикла. Это должно освободить место. Другой трюк «best-practice» заключается в том, чтобы выйти из цикла обработки данных, которые не должны быть в цикле. Как вы, кажется, обнаружили.

Я запустил PHP скрипта, которые требуют до 1 ГБ памяти. Вы можете установить ограничение памяти для каждого скрипта, на самом деле, с ini_set(‘memory_limit’, ‘1G’);

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

Похожие вопросы:

Я хочу в основном остановить сборщик мусора от удаления моих сеансов php, чтобы я мог вручную удалить их сам при определенных условиях. Как я могу остановить сборщик мусора?

Читать еще:  1 понятие налога и сбора

Может кто-нибудь объяснить, как работает сборщик мусора G1, пожалуйста? Я пока нигде не смог найти исчерпывающих описаний easy-to-understand. Спасибо

Может кто-нибудь, пожалуйста, объясните мне. Каков алгоритм работы сборщика мусора? Как сборщик мусора работает в фоновом режиме?

Запускается ли сборщик мусора в отдельном процессе? Например: Если мы попытаемся измерить время процесса, занимаемое каким-то фрагментом кода, и в течение этого времени сборщик мусора начнет сборку.

От MSDN: Когда объект недоступен, сборщик мусора считает объект мусором. Затем, когда сборщик мусора перемещает запись объекта из очереди завершения в очередь свободного доступа, объект больше не.

Как работает сборщик мусора Java и какой алгоритм лучше, какой сборщик мусора Java use.Morevoer, В Java случаются утечки памяти или нет?

Как именно работает сборка мусора redux. Мы все знаем, что redux способствует неизменности. Так что же происходит с устаревшими экземплярами состояния? Например, в моем редукторе, если у меня есть.

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

У меня очень плохой код, написанный в php. Он потребляет много памяти, а затем тормозит, очищает сеансы и кланяется! Мне нужно знать, когда запускается сборщик мусора, чтобы я мог доказать, что это.

Я пытаюсь понять, как работает сборщик мусора golang, когда код golang компилируется,и я думаю, что при использовании go run также. Я считаю, что go run немного более прямолинейный и просто.

Информационный портал по безопасности

Ломаем сбор мусора и десериализацию в PHP

Эй, PHP, эти переменные выглядят как мусор, согласен?
Нет? Ну, посмотри-ка снова…

tl;dr:
Мы обнаружили две use-after-free уязвимости в алгоритме сбора мусора в PHP:

  • Одна присутствует во всех версиях PHP 5 >= 5.3 (исправлена в PHP 5.6.23).
  • Вторая — во всех версиях PHP >= 5.3, включая версии PHP 7 (исправлена в PHP 5.6.23 и PHP 7.0.8).

Уязвимости могут удалённо применяться через PHP-функцию десериализации. Используя их, мы отыскали RCE на pornhub.com, за что получили премию в 20 000 долларов плюс по 1000 долларов за каждую из двух уязвимостей от комитета Internet Bug Bounty на Hackerone.

Занимаясь проверкой Pornhub, мы обнаружили две критические утечки в алгоритме СМ (How we broke PHP, hacked Pornhub and earned $20,000). Речь идёт о двух важных use-after-free уязвимостях, которые проявляются при взаимодействии алгоритма СМ с определёнными PHP-объектами. Они приводят к далеко идущим последствиям вроде использования десериализации для удалённого выполнения кода на целевой системе. В статье мы рассмотрим эти уязвимости.

После фаззинга десериализации и анализа интересных случаев мы выделили два доказательства возможности use-after-free уязвимостей. Если вам интересно, как мы к ним пришли, то почитайте материал по ссылке. Один из примеров:

Наверное, вы думаете, что результат будет примерно таким:

В любом случае после исполнения мы видим, что внешний массив (на него ссылается $outer_array ) освобождён, а его zval перезаписан zval’ом $filler2 . И в качестве результата мы получаем bbbb . Возникают следующие вопросы:

  • Почему вообще освобождается внешний массив?
  • Что делает gc_collect_cycles() и действительно ли необходимо вызывать её вручную? Это очень неудобно для удалённого использования, потому что многие скрипты и установки вообще не вызывают эту функцию.
  • Даже если мы сможем вызвать её в ходе десериализации, будет ли работать этот пример?

Похоже, вся магия происходит в функции gc_collect_cycles , которая вызывает сборщик мусора PHP. Нам нужно лучше понять её, чтобы разобраться с этим таинственным примером.

Обратите внимание: нужны базовые знания о внутренностях PHP, управлении памятью и вещах вроде zval’а и подсчёта ссылок. Если вы не знаете, что это, то сначала ознакомьтесь с основами: PHP Internals Book – Basic zval structure и PHP Internals Book – Memory managment.

Zend/zend_gc.c. При каждом уничтожении zval’а, т. е. когда он сбрасывается (unset), применяется алгоритм СМ, который проверяет, массив это или объект. Все остальные типы данных (примитивы) не могут содержать циклические ссылки. Проверка реализована путём вызова функции gc_zval_possible_root . Любой такой потенциальный zval называется root и добавляется в список gc_root_buffer .

Эти шаги повторяются до тех пор, пока не будет выполнено одно из условий:

  • gc_collect_cycles() вызван вручную.
  • Заполнился объём памяти для хранения мусора. Это означает, что в root-буфере сохранено 10 000 zval’ов и сейчас будет добавлен ещё один. Ограничение 10 000 по умолчанию прописано в GC_ROOT_BUFFER_MAX_ENTRIES в заглавной секции Zend/zend_gc.c. Следующий zval снова вызовет gc_zval_possible_root , а тот уже вызовет gc_collect_cycles для обработки и очистки текущего буфера, чтобы можно было сохранять новые элементы.

стандартный для PHP 5.6 .gdbinit, а также кастомную подпрограмму для дампинга содержимого буфера сборщика мусора.

Теперь установим точку прерывания в gc_mark_roots и gc_scan_roots , чтобы посмотреть состояние всех соответствующих счётчиков ссылок.

Нам нужно найти ответ на вопрос: почему освобождается внешний массив? Загрузим php-процесс в gdb, установим точки прерывания и выполним скрипт.

Как видите, после десериализации оба массива (внутренний и внешний) добавлены в буфер сборщика мусора. Если мы продолжим и прервёмся на gc_scan_roots , то получим следующие состояния счётчиков ссылок:

Читать еще:  Окраска сборных моделей

gc_mark_roots действительно декрементировал все счётчики до нуля. Следовательно, эти узлы на следующих этапах могут быть маркированы белым и позднее освобождены. Но возникает вопрос: почему в первом случае счётчики обнулились?

появилась в PHP 7.1.0 alpha2 почти сразу после выхода. Получается, что уязвимости подвержены все версии PHP >= 5.3 и PHP php_zip_get_properties . Тем самым мы освобождаем какой-то конкретный элемент. Посмотрите на пример:

Нужно упомянуть, что в нормальных условиях невозможно создать ссылки на zval’ы, которые ещё не десериализованы. Эта полезная нагрузка использует небольшой трюк, позволяющий обойти ограничение:

[. ] i:1;N; [. ] s:8:»filename»;i:1337; [. ] i:1;R:REF_TO_FILENAME; [. ]

Здесь создаётся запись NULL с индексом 1, которая позднее перезаписывается ссылкой на имя файла. Сборщик мусора увидит только “i:1;REF_TO_FILENAME; […] s:8:”filename”;i:1337; […]”. Этот трюк необходим для уверенности в том, что счётчик ссылок целочисленного zval’а “filename” был ослаблен, прежде чем начнут действовать какие-либо побочные эффекты.

Заключение

Подготовка этих багов к удалённому использованию была очень непростой задачей. Стоило решить одну проблему, как возникала новая. В статье мы рассмотрели один из подходов к решению достаточно сложной проблемы. Последовательно задавая себе продуманные вопросы, концентрируясь на постепенном получении каждого ответа и разбирая определения, мы наконец справились с трудностями и достигли цели.

Было интересно понаблюдать за взаимодействием двух совершенно не связанных друг с другом PHP-компонентов: десериализатора и сборщика мусора. Лично я получил море удовольствия и многое узнал, анализируя их поведение. Так что могу порекомендовать вам воспроизвести всё вышесказанное для обучения. Это особенно важно: статья достаточно длинная, но даже в ней я не осветил ряд деталей.

В сценарии применялся десериализатор, но можно обойтись и без него, по крайней мере для локального использования уязвимостей. Это отличает их от обычных, лежащих на поверхности уязвимостей, которые применялись для аудита десериализации в более ранних версиях PHP. В любом случае, как говорилось в начале статьи: никогда не прибегайте к десериализации с пользовательским вводом, а лучше опирайтесь на менее сложные методы вроде JSON.

Эксперимент подтвердил, что мы можем использовать одну из обсуждённых уязвимостей для удалённого исполнения кода на pornhub.com. Это делает сборщика мусора в PHP интересным кандидатом для атаки.


Сборка мусора zval’ов пошла как-то не так.

Php сборщик мусора

Автоматическая сборка мусора (выделенной и более не используемой памяти) предназначена для облегчения жизни программисту, позволяя не заботиться о возможных утечках памяти (например, удалить выделенный в куче объект). С одной стороны — безусловно благие намерения, с другой — получение «бесплатных тормозов» при работе оного сборщика мусора (garbage collector). Хотя, наверное, приятно не обращать внимание на такие досадные мелочи, как удаление выделенной памяти 🙂 .

Выделение памяти

По идее, память из кучи в среде .NET Runtime выделяется достаточно быстро. Системе всего лишь необходимо удостовериться, что в управляемой куче достаточно памяти для запрашиваемого объекта, вернуть указатель на эту память и перевести указатель на следующий после последнего объекта адрес. Соответственно, простота выделения памяти сопровождается некоторым усложнением очистки.

Для повышения быстродействия большие объекты (>20 КБайт) выделяются из отдельной кучи.

Пометка и сжатие

Работа сборщика мусора основана на алгоритме «пометки и сжатия». Сборщик мусора начинает свою работу с просмотра корневых объектов (включая глобальные, статические, локальные объекты и регистры процессора) и нахождения всех объектов, ссылки на которые присутствуют в корневых объектах. Объекты, используемые на момент сборки мусора, помечаются, а все остальные объекты в системе считаются ненужными.

Процесс сборки мусора завершается копированием всех используемых объектов в начало управляемой кучи и соответствующей модификацией указателей на объекты. Затем указатель устанавливается на следующий доступный блок после последнего используемого объекта.

Поскольку сборщик мусора перемещает объекты и изменяет ссылки на них, в это время в системе не может выполняться никаких других операций. То есть во время работы сборщика вся полезная работа прекращается.

Поколения

Перебор всех объектов, на которые существуют ссылки, обходится дорого. Более того, основная часть работы будет потрачена впустую — чем старше объект, тем больше вероятность того, что он продолжает использоваться. Соответственно, ссылки на новые объекты с большей вероятностью перестают использоваться.

Для воплощения этого принципа в сборщике .NET использована концепция поколений (generations). Объекты в куче делятся на три поколения.

Поколение 0 составляют объекты, никогда не проходившие сборку мусора. Объекты поколения 1 пережили одну сборку мусора, а объекты поколения 2 прошли сборку мусора несколько раз.

Когда среде потребуется выполнить сборку мусора, она сначала выполняет сборку мусора среди объектов поколения 0. Именно к этому поколению относится большая часть неиспользуемых объектов, что обеспечивает освобождение максимальной памяти при минимальных затратах. Если сборка мусора в этом поколении не освобождает достаточно памяти, система переходит к поколению 1 и, если потребуется, — к поколению 2.

Пусть в табличке изображены находящиеся в куче объекты перед сборкой мусора. Числовой суффикс обозначает поколение объекта; первоначально все объекты относятся к поколению 0. Активными являются только те объекты, которые находятся в таблице. Допустим, необходимо выделить память для дополнительных объектов.

Ссылка на основную публикацию
Adblock
detector