Php сколько памяти потребляет скрипт

Обновлено: 06.05.2024

Я заметил, что есть большая разница между тем, что top или же ps сообщает об использовании памяти для процесса PHP по сравнению с тем, что сам процесс считает своим использованием (с memory_get_usage ).

Сколько памяти фактически использует процесс?

При запуске следующего кода вместе с одним из моих приложений:

Выход в случайной точке был:

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

Когда я устанавливаю ограничение памяти PHP, например, По 128 МБ для каждого из этих демонов процессы будут убиты только после того, как они достигнут 128 МБ в соответствии с собственными измерениями PHP. Однако согласно ps к тому времени процессы будут использовать около 200 МБ каждый.

Решение

Следует подчеркнуть, что именно значения, представленные ps а также memory_get_usage(true) являются.

ps -o rss сообщает фактическое Размер резидентного набора. Полагаться на это значение довольно сложно, так как оно не включает в себя выгруженную память. В общем, вы хотите военный корабль США, Уникальный размер набора, которая в основном не разделяемая память (посмотрите на smem(8) для этого). Это количество неразделенной памяти, которую ядро ​​фактически отобразило для этого процесса, то есть физически представит неразделенную память либо в ОЗУ, либо в файлах подкачки. Это как можно ближе к «реальному» использованию памяти. [Также обратитесь к /proc/$PID/smaps для подробного обзора, как упомянуто в ответе IVO GELOV, где вы могли бы технически подсчитать память, которую вы хотите считать сами, анализируя этот виртуальный файл.]

Если вы пройдете true в качестве первого параметра, т.е. memory_get_usage(true) , он возвращает общий объем памяти, запрошенный из системы диспетчером внутренней памяти PHP. Это число в целом немного, но не намного выше, чем memory_get_usage(false) , Это также число, которое memory_limit Настройка INI сравнивается с

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

Другие решения

memory_get_usage сообщает о памяти, выделенной процессом PHP для запуска вашего скрипта. ps сообщает о памяти, используемой самим процессом PHP, включая память, используемую для вашего скрипта. Процесс PHP использует много внешних библиотек, которые могут распределять свою память без того, чтобы процесс PHP не знал об этом.

Так memory_get_usage а также ps по своей сути измеряют разные вещи и должны сообщать разные цифры. Все сводится к тому, как вы определяете «фактическое использование памяти». Я понимаю, что в вашем случае вас больше интересует использование памяти процессом PHP. Тогда вывод ps более актуально для вас. Но вы можете легко обнаружить, что даже значение RSS, сообщаемое ps не так черно-белый в мире современных операционных систем и общих воспоминаний.

Вы можете найти интересные вещи, когда выполните одну из следующих команд:


На этот пост меня вдохновило исследование потребления памяти для моего текущего большого проекта на ZendFramework. Как обычно, по результатам исследования, я был шокирован нашей программистской самонадеянностью, которая нередко присутствует, когда мы пишем что-либо большое на PHP. Да и, наверное, не только на PHP.

Но обо всём по порядку.

Как будем измерять

Для начала определимся, как мы будем измерять «вес». Вот шаблон:

Такой шаблон подходит для измерения новой выделяемой памяти, то есть памяти под переменные. А вот измерить, сколько едят определения, то есть описания функций и классов, таким подходом нельзя, так как они заносятся в память до начала выполнения скрипта. Для того чтобы измерить определения, воспользуемся следующим шаблоном:

  • Длины абсолютного пути до файла
  • Каким по счёту этот файл был включён (каждые 8, 16, 32, 64 и т.д. идёт дополнительное выделение под внутренние структуры)
  • Заполненностью внутренних структур самого PHP, которые периодически довыделяют себе памяти на будущее.

Сколько весят «объекты»



Можно заметить, что сборка Gentoo потребляет на 10-20% меньше памяти, а в редких случаях экономия доходит до 50%. Видимо, размер внутренних структур зависит от оптимизаций для процессора. Для экперимента я пересобирал php с разными вариантами CFLAGS, но он от этого не стал потреблять больше. Видимо разница проявляется не из-за пересборки самого PHP, а из пересборки стандартных Сишных библиотек.



Как было отмечено выше, точно измерить $include_overhead сложно, поэтому если вы запустите данные тесты, то у вас могут получится так, что потребление памяти будет прыгать на 4, 8, 12, 16 байт, даже в тестах, которые должны потреблять одинаково. Не стоит акцентировать на этом внимания. Я запускал тесты в разном порядке и более-менее установил истинное потребление памяти.

Поговорим о тестах, связанных с ZendFramework. Загрузка определений классов Zend`а в память отжирает существенные ресурсы, тогда как ссылки на объекты уже потребляют не так много. Controller2 нужен, чтобы проверить, сколько будет отжирать аналогичный контроллер, если все промежуточные классы уже в памяти. Model2 создана для этих же целей.
В потенциале использование PHP акселератора сэкономит нам память на всех определениях, ибо они уже будут храниться в памяти. Давайте проверим это утверждение.

Тестирование акселераторов

Для тестирования был взят APC, и тесты запускались через web с помощью скрипта:

Результаты приведены только тестов, где акселератор оказывает влияние:

myclass.a.empty Описание класса A 840 672 1480 1256
myclass.aa.interface Описание интерфейса A 856 676 1512 1264
myclass.ab.final Описание финального класса AB 844 672 1488 1256
myclass.ac.abstract Описание абстрактного класса AC 852 680 1504 1264
myclass.b.extended.empty Описание класса B, расширяющего A 912 700 1512 1264
myclass.c.empty.namespace Описание пустого неймспейса C 176 -16 184 -72
myclass.d.construct Описание класса D с конструктором 1256 960 2448 1736
myclass.dd.method Описание класса DD с методом 1268 968 2432 1728
myclass.ddd.private.var Описание класса DDD с приватной переменной 1140 964 2000 1760
myclass.dddd.public.var Описание класса DDDD с публичной переменной 1132 952 2000 1760
myclass.ddddd.static.var Описание класса DDDDD со статической переменной 1124 952 2000 1760
myclass.e.extended.destruct Описание класса E с деструктором, расширяющим класс D 1528 1228 2888 2160
myclass.z.free_memory Удаление ссылок $e, $f -332 -548 -784 -1024
zend.a.init.autoload Инициализация autoload для ZendFramework 127 596 16 196 276 440 28 992
zend.a.init.model Инициализация адаптера по умолчанию для базы 1 018 564 251 840 2 081 696 479 280
zend.extended.controller1 Определение контроллера от Zend_Controller_Action. Попутно происходит подгрузка стандартных зендовских классов 378 464 66 804 809 608 120 864
zend.extended.controller2 Определение контроллера. Класы Zend уже подгружены, смотрим сколько весит наш класс 11 476 11 140 19 792 19 056
zend.extended.model1 Определение модели от Zend_Db_Table. Попутно происходит подгрузка стандартных зендовских классов. 28 080 25 676 48 704 42 944
zend.extended.model2 Определение модели. Класы Zend уже подгружены, смотрим, сколько весит наш класс 28 080 25 704 48 672 42 960



Я также производил некоторые тесты с xcache и заметил 2 отличия от APC. Во-первых: xcache проигрывает (почти всегда) на 10-15% по экономии памяти. А во-вторых: xcache сразу отдаёт файлы из кеша, тогда как APC — только после повторного обращения. Хоть и бесполезное, но преимущество.



Сразу отмечу, в результатах разброс гораздо больше, чем при тестировании без акселератора, поскольку файлы не переименовывались и $include_overhead рассчитывался с большой ошибкой.

Как мы видим, акселератор хоть и экономит нам память для определений, но не полностью, поскольку PHP, видимо, переносит какие-то куски из кеша в текущую сессию.
Теперь перейдем от абстрактных тестов к вполне реальным.

В предыдущем разделе нами уже говорилось, что простой сбор корней меньше влияет на производительность. Хотя запись корней в буфер по сравнению с полным отсутствием таковой в PHP 5.2 работает медленней, другие изменения в работе PHP 5.3 сделали эту потерю производительности незаметной.

Есть две основные области влияющие на производительность: уменьшение размера используемой памяти и замедление работы при сборке мусора. Рассмотрим их.

Уменьшение размера используемой памяти

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

<?php
class Foo
public $var = '3.14159265359' ;
>

for ( $i = 0 ; $i <= 100000 ; $i ++ )
$a = new Foo ;
$a -> self = $a ;
if ( $i % 500 === 0 )
echo sprintf ( '%8d: ' , $i ), memory_get_usage () - $baseMemory , "\n" ;
>
>
?>

Сравнение использования памяти в PHP 5.2 и PHP 5.3

В этом очень академическом примере мы создаём объект, свойство a которого задаётся ссылкой на сам объект. Когда в скрипте в следующей итерации цикла переопределяется переменная $a , то происходит типичная утечка памяти. В данном случае пропадают два контейнера zval (контейнер объекта и контейнер свойства объекта), но определяется только один корень - удалённая переменная. Как только пройдут 10 000 итераций (максимально в корневом буфере будет 10 000 корней), то запустится механизм сборки мусора и память, занимаемая этими корнями, будет освобождена. Этот процесс хорошо виден на графике использования памяти для PHP 5.3: после каждых 10 000 итераций график проседает. Сам по себе механизм в данном примере совершает не так много работы, потому что структура утечек слишком проста. Из графика видно, что максимальное использование памяти в PHP 5.3 составило около 9 Мб, тогда как в PHP 5.2 оно продолжает возрастать.

Замедление работы

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

<?php
class Foo
public $var = '3.1415962654' ;
>

for ( $i = 0 ; $i <= 1000000 ; $i ++ )
$a = new Foo ;
$a -> self = $a ;
>

echo memory_get_peak_usage (), "\n" ;
?>

Мы запустим скрипт два раза: с включённой опцией zend.enable_gc и без неё.

На тестовой машине первая команда примерно выполняется 10.7 секунд, а вторая примерно 11.4 секунды. Это примерно на 7% медленнее. Однако, максимальное использование памяти скриптом уменьшилось на 98% с 931 Мб до 10 Мб. Этот тест не очень научный, но он действительно демонстрирует преимущество по использованию памяти, обеспечиваемое сборщиком мусора. Также хорошо то, что замедление для этого скрипта всегда примерно 7%, тогда как экономия памяти увеличивается все больше и больше при нахождении нового мусора.

Внутренняя статистика сборщика мусора

Можно получить немного больше информации о том, как механизм сборки мусора выполняется в PHP. Но для этого вам необходимо пересобрать PHP для включения теста производительности и кода для дополнительного сбора данных. Необходимо установить переменную окружения CFLAGS в значение -DGC_BENCH=1 до выполнения команды ./configure с вашими параметрами. Следующие команды должны сработать:

При запуске вышеприведённого примера с обновлённым PHP, можно увидеть следующую информацию по завершении работы скрипта:

Наиболее полезная статистика отображена в первом блоке. Можно увидеть, что механизм сборки мусора был запущен 110 раз, и суммарно было освобождено свыше 2 миллионов записей в памяти. Если сборка мусора была запущена хотя бы раз, то максимальное число корней в буфере всегда будет равно 10 000.

Заключение

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

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

Преимущества наиболее очевидны для долгоработающих скриптов, таких как большие наборы тестов или демоны. Новый механизм также должен снизить утечки памяти для приложений » PHP-GTK, которые выполняются обычно дольше, чем веб-скрипты.

Настройки потребления ресурсов в PHP скриптах можно установить в главном конфигурационном файле php.ini, а также в самих скриптах.

В файле php.ini за это отвечают директивы из раздела Resource Limits (ограничение потребления ресурсов).

Как увеличить память для PHP скриптов

Для этого в файле php.ini найдите и отредактируйте директиву:

Эта директива задаёт максимальное время в секундах, в течение которого скрипт должен полностью загрузиться. Если этого не происходит, парсер завершает работу скрипта. Этот механизм помогает предотвратить зависание сервера из-за плохо написанного скрипта. По умолчанию на загрузку даётся 30 секунд. Если PHP запущен из командной строки, это значение по умолчанию равно 0.

На максимальное время выполнения не влияют системные вызовы, потоковые операции и т.п.

При работе в безопасном режиме эту настройку нельзя изменить функцией ini_set(). Если значение все же нужно изменить, надо либо выключить безопасный режим, либо изменить значение прямо в php.ini.

Веб-серверы обычно имеют свои настройки тайм-аута, по истечении которого сами завершают выполнение скрипта PHP. В Apache есть директива Timeout, в IIS есть функция CGI timeout. В обоих случаях по умолчанию установлено 300 секунд. Точные значения можно узнать из документации к веб-серверу.

Функция для увеличения и ограничения времени выполнения PHP

Функция set_time_limit ограничивает время выполнения скрипта.

Она задает время в секундах, в течение которого скрипт должен завершить работу. Если скрипт не успевает, вызывается фатальная ошибка. По умолчанию дается 30 секунд, либо время, записанное в настройке max_execution_time в php.ini (если такая настройка установлена).

При вызове set_time_limit() перезапускает счетчик с нуля. Другими словами, если тайм-аут изначально был 30 секунд, и через 25 секунд после запуска скрипта будет вызвана функция set_time_limit(20), то скрипт будет работать максимум 45 секунд.

  • СЕКУНДЫ (максимальное время выполнения в секундах. Если задан ноль, время выполнения неограничено)

Возвращаемые значения: возвращает TRUE в случае успеха, иначе FALSE.

Внимание: эта функция не работает, если PHP работает в безопасном режиме. Обойти это ограничение можно только выключив безопасный режим или изменив значение настройки в php.ini.

Замечание: функция set_time_limit() и директива max_execution_time влияют на время выполнения только самого скрипта. Время, затраченное на различные действия вне скрипта, такие как системные вызовы функции system(), потоковые операции, запросы к базам данных и т.п. не включаются в расчет времени выполнения скрипта. Это не относится к системам Windows, где расчитывается абсолютное время выполнения.

Увеличение выделенной памяти для PHP скриптов

Директива в файле php.ini

задаёт максимальный объем памяти в байтах, который разрешается использовать скрипту. Это помогает предотвратить ситуацию, при которой плохо написанный скрипт съедает всю доступную память сервера. Для того, чтобы убрать ограничения, установите значение этой директивы в -1.

В версиях до PHP 5.2.1 для использования этой директивы, она должна была быть указана на этапе компиляции. Так, ваша строка конфигурации должна была включать: --enable-memory-limit. Эта опция компиляции была также необходима для использования функций memory_get_usage() и memory_get_peak_usage() до версии 5.2.1.

Если используется целое число, то значение измеряется байтами. Вы также можете использовать сокращённую запись.

Доступные опции: K (для килобайт), M (для мегабайт) и G (для гигабайт; доступна начиная с PHP 5.1.0); они регистронезависимы. Все остальное считается байтами. 1M равно одному мегабайту или 1048576 байтам. 1K равно одному килобайту или 1024 байтам. Эти сокращения вы можете использовать в php.ini и в функции ini_set(). Обратите внимание, что числовое значение приводится к типу integer; например, 0.5M интерпретируется как 0.

Увеличение времени парсинга данных из запроса.

Директива в файле php.ini

задаёт максимальное время в секундах, в течение которого скрипт должен разобрать все входные данные, переданные запросами вроде POST или GET. Это время измеряется от момента, когда PHP вызван на сервере до момента, когда скрипт начинает выполняться. Значение по умолчанию -1, что означает, что будет использоваться max_execution_time. Если установить равным 0, то ограничений по времени не будет.

При запуске в командной строке значение директивы установлено на -1 (неограниченно).

Увеличение глубины вложенности входных переменных

Директива в файле php.ini

задаёт максимальную глубину вложенности входных переменных (то есть $_GET, $_POST.). По умолчанию данная директива закомментирована.

Ограничение на количество входных переменных

Директива в файле php.ini

определяет, входных переменных может быть принято в одном запросе (ограничение накладывается на каждую из глобальных переменных $_GET, $_POST и $_COOKIE отдельно). Использование этой директивы снижает вероятность сбоев в случае атак с использованием хеш-коллизий. Если входных переменных больше, чем задано директивой, выбрасывается предупреждение E_WARNING, а все последующие переменные в запросе игнорируются. По умолчанию данная директива закомментирована.

Внимание: после внесения изменений в файл php.ini необходимо перезагрузить веб-сервер, чтобы изменения вступили в силу.

Проверка использование ресурсов

Функция getrusage получает информацию об использовании текущего ресурса.

Возвращаемые значения: возвращает ассоциативный массив, содержащий данные возвращённые из системного вызова. Имена элементов соответствуют документированным именам полей.

Пример использования getrusage():


Увеличение разрешённого размера файлов для загрузки на сервер

Кроме описанных ограничений на потребление непосредственных ресурсов веб-сервера, также имеются ограничения, которые оказывают косвенное воздействие на ресурсы: например, загрузка слишком большого файла на сервер может потребовать большого количества ресурсов для его обработки, либо привести к переполнению дискового хранилища сервера. Поэтому существуют дополнительные лимиты, включённые в другие разделы конфигурационного файла помимо Resource Limits.

В частности, директива

устанавливает максимальный размер закачиваемого файла.

Если используется целое число, значение измеряется байтами. Вы также можете использовать сокращённую запись, которая описана чуть выше.

Максимально допустимый размер данных, отправляемых методом POST

Методом пост могут отправляться как текстовые данные (например, при отправке комментария на сайт), так и файлы. Если вы увеличили значение upload_max_filesize чтобы иметь возможность загружать файлы большего размера на сайт, то также нужно увеличить и значение директивы:

Замечание: PHP разрешает сокращения значений байт, включая K (кило), M (мега) и G (гига). PHP автоматически преобразует все эти сокращения. Будьте осторожны с превышением диапазона 32-битных целых значений (если вы используете 32-битную версию), так как это приведёт к ошибке вашего скрипта.

Для полного снятия лимитов значение можно установить на 0.

Значение этой настройки игнорируется, если чтение данных POST отключено с помощью enable_post_data_reading.

Увеличение количества файлов, выгружаемых на сайт за один раз

устанавливает максимально разрешённое количество одновременно закачиваемых файлов. Начиная с PHP 5.3.4, пустые поля загрузки не рассматриваются этим ограничением.

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

Читайте также: