php сколько памяти потребляет скрипт
Как увеличить память и время для PHP скриптов
Настройки потребления ресурсов в 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, где расчитывается абсолютное время выполнения.
Обе set_time_limit(…) и ini_set(‘max_execution_time’,…); не учитывают время, потраченное функциями sleep, file_get_contents, shell_exec, mysql_query и некоторыми другими.
Увеличение выделенной памяти для 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
Ограничение на количество входных переменных
Директива в файле php.ini
Внимание: после внесения изменений в файл php.ini необходимо перезагрузить веб-сервер, чтобы изменения вступили в силу.
Проверка использование ресурсов
Функция getrusage получает информацию об использовании текущего ресурса.
Возвращаемые значения: возвращает ассоциативный массив, содержащий данные возвращённые из системного вызова. Имена элементов соответствуют документированным именам полей.
Пример использования getrusage():
Увеличение разрешённого размера файлов для загрузки на сервер
Кроме описанных ограничений на потребление непосредственных ресурсов веб-сервера, также имеются ограничения, которые оказывают косвенное воздействие на ресурсы: например, загрузка слишком большого файла на сервер может потребовать большого количества ресурсов для его обработки, либо привести к переполнению дискового хранилища сервера. Поэтому существуют дополнительные лимиты, включённые в другие разделы конфигурационного файла помимо Resource Limits.
В частности, директива
устанавливает максимальный размер закачиваемого файла.
Если используется целое число, значение измеряется байтами. Вы также можете использовать сокращённую запись, которая описана чуть выше.
Максимально допустимый размер данных, отправляемых методом POST
Методом пост могут отправляться как текстовые данные (например, при отправке комментария на сайт), так и файлы. Если вы увеличили значение upload_max_filesize чтобы иметь возможность загружать файлы большего размера на сайт, то также нужно увеличить и значение директивы:
которая устанавливает максимально допустимый размер данных, отправляемых методом POST. Это значение также влияет на загрузку файлов. Для загрузки больших файлов это значение должно быть больше значения директивы upload_max_filesize. В сущности, memory_limit должна быть больше чем post_max_size. Если используется integer, значение измеряется байтами. Вы также можете использовать сокращённую запись, которая описана выше. Если размер POST-данных больше чем post_max_size, суперглобальные переменные $_POST и $_FILES будут пустыми. Это можно отследить различными способами, например передав $_GET переменную в скрипт, обрабатывающий данные, т.е.
Сколько памяти расходует скрипт PHP?
потом потребление падает при каждом выполнении
улучшает производительность PHP путём сохранения скомпилированного байт-кода скриптов в разделяемой памяти, тем самым избавляя PHP от необходимости загружать и анализировать скрипты при каждом запросе.
Это расширение доступно по умолчанию с PHP 5.5.0
Попробуйте упростить работу РНР, сделайте преобразование XML в иерархический массив.
Накладные расходы на хранение будут в разы меньше.
Еще меньше если не будете использовать иерархию, а сформируете хеш-массив всех элементов.
Попробуйте заменить все unset на приведение переменной в значение равной null
$a = null;
(https://stackoverflow.com/a/20679276)
Для того чтобы привести xml к нужному мне виду (ассоциативный массив) я использовал последовательно связку встроенных функций из simplexml_load_file, json_encode json_decode (true), и проблема была именно в них.
Замер (приблизительный) переменной, полученной после simplexml_load_file дал вполне себе вменяемый расход
памяти. Соответственно, проблема была с функциями которые работают с JSON. Сразу же вспомнил, что скрипт всегда падал на json_decode (true), но в тот момент думал, что она последняя в очереди на обработку и ей просто не хватает выделанной памяти.
Далее. Ни unset () ни присваивание null переменным которые получались в ходе работы функций не оказали значимого эффекта.
Проблема устранена после того как исключил json_encode json_decode (true) из кода (получаю массив из xml иным способом, остальной код тот же, файлы те же).
Потребление памяти стабильно и в разумных пределах, скрипт не падает, выполняется за 2-3 секунды (не указал это ранее. но время выполнение скрипта тоже варьировалось от нескольких секунд до минуты.)
Работа с памятью (и всё же она есть)
Существует распространенное мнение, что «рядовому» PHP разработчику практически не нужно заботиться об управлении памятью, однако «заботиться» и «знать» всё же немного разные понятия. Попытаюсь осветить некоторые аспекты управлению памятью при работе с переменными и массивами, а также интересные «подводные камни» внутренней оптимизации PHP. Как вы сможете убедиться, оптимизация это хорошо, но если не знать как именно она «оптимизирует», то можно столкнуться с «неочевидными граблями», которые могут вас заставить изрядно понервничать.
Общие сведения
Небольшой ликбез
Переменная в PHP как бы состоит из двух частей: «имени«, которое хранится в hash_table symbol_table, и «значения«, которое хранится в zval контейнере.
Такой механизм позволяет создавать несколько переменных ссылающихся на одно значение, что в отдельных случаях позволяет оптимизировать потребление памяти. О том, как это выглядит на практике будет написано далее.
Наиболее частыми элементами кода, без которых сложно себе представить более менее функциональный скрипт, являются следующие моменты:
— создание, присвоение и удаление переменных (чисел, строк и т.п.),
— создание массивов и их обход (в качестве примера будет использована функция foreach),
— передача и возврат значений для функций/методов.
Именно об этих аспектах работы с памятью и будет последующее описание. Получилось достаточно объемно, но ничего мега-сложного не будет и всё будет достаточно просто, очевидно и с примерами.
Первый пример работы с памятью
И простой первый пример теста потребления памяти для строки:
include ( ‘func.php’ ) ;
echo «String memory usage test.\n\n» ;
$base_memory_usage = memory_get_usage ( ) ;
$base_memory_usage = memory_get_usage ( ) ;
Примечание: несомненно код является неоптимизированным с точки зрения работоспособности, но в данном случае нам исключительно важна наглядность потребления памяти, для которой и реализовано данное представление.
Результат кода вполне очевиден:
Start
Bytes diff: 0
String value setted
Bytes diff: 15448
String value unsetted
Bytes diff: 0
Тот же самый пример, но вместо unset($a) используем $a=null;:
Start
Bytes diff: 0
String value setted
Bytes diff: 15448
String value set to null
Bytes diff: 76
Как видите, переменная не была полностью уничтожена. Под нее остается выделенным еще 76 байт.
Достаточно прилично, если учесть, что ровно столько же выделяется и под переменные типа boolean, integer, float. Речь идет не об объеме памяти, выделяемой под значение переменной, а о полном потреблении памяти для хранения сведений о присвоенной переменной (zval контейнер со значением и само имя переменной).
Так что если вы хотите освободить память при помощи присвоения, то не является принципиальным присвоение именно null значения. Выражение $a=10000; даст тот же результат для расхода памяти.
В документации PHP сказано, что приведение к null уничтожит переменную и ее значение, однако, по данному скрипту видно что это не так, что собственно является багом (документации).
Зачем использовать присвоение null, если можно unset()?
Присвоение — это присвоение, (спасибо КО), то есть изменяется значение переменной, соответственно, если новое значение требует меньше памяти, то она высвобождается сразу, однако это требует вычислительных ресурсов (пусть и сравнительно немного).
unset() в свою очередь освобождает память, выделенную под имя переменной и ее значение.
Отдельно стоит упомянуть момент, что unset() и присвоение null совершенно по разному работают со ссылками на переменные. Unset() уничтожит только ссылку, в то время как присвоение null изменит значение, на которое ссылаются имена переменных, соответственно все переменные станут ссылаться на значение null.
Примечание:
Встречается заблуждение, что unset() является функцией, однако, это не верно. unset() — это языковая конструкция (как например if), о чем прямо сказано в документации, соответственно ее нельзя использовать для обращения через значение переменной:
Немного дополнительной информации для праздных размышлений (при изменении примера выше):
$a = array();
выделит 164 байта, unset($a) всё вернет.
class A < >
$a = new A();
выделит 184 байта, unset($a) всё вернет.
$a = new stdClass();
выделит 272 байта, но после unset($a) «утекут» 88 байт (куда именно и почему они утекли, мне пока не удалось выяснить).
Пока приведенные примеры не являются критичными в плане потребления памяти, так как строковые и числовые значения достаточно очевидно хранятся и обрабатываются. Всё становится значительно хуже, когда в ход идут массивы (объекты тоже имеют целый ряд особенностей, однако для этого уже потребуется отдельная статья).
Массивы
Массивы в PHP «съедают» достаточно памяти, и именно в них как правило хранят значительные объемы данных при обработке, поэтому следует очень аккуратно относиться к работе с ними. Однако, работа с массивами в PHP имеет свои «прелести оптимизации» и об одном из таких моментов, связанных с потреблением памяти, стоит упомянуть.
? php
include ( ‘func.php’ ) ;
echo «Array memory usage example.» ;
$base_memory_usage = memory_get_usage ( ) ;
$base_memory_usage = memory_get_usage ( ) ;
Посмотрите на вывод:
Array memory usage example.Base usage.
Bytes diff: 0
Array is set.
Bytes diff: 61940
In FOREACH cycle.
Bytes diff: 77632
In FOREACH cycle.
Bytes diff: 93032
In FOREACH cycle.
Bytes diff: 108432
In FOREACH cycle.
Bytes diff: 123832
Usage right after FOREACH.
Bytes diff: 61940
Array unset.
Bytes diff: 0
Получается, что в последней итерации цикла foreach в данном случае потребление массивом памяти возросло в два раза, хотя по самому коду это не очевидно. Но сразу после цикла, потребление памяти вернулось к прежнему значению. Чудеса да и только.
Причиной тому является оптимизация использования массива в цикле. На время работы цикла, при попытке изменить исходный массив, неявно создается копия структуры массива (но не копия значений), которая и становится доступной по завершению цикла, а исходная структура уничтожается. Таким образом, в вышеприведенном примере, если вы присваиваете новые значения исходному массиву, то они не будут заменены сразу, а для них будет выделена отдельная память, которая будет возвращена по выходу из цикла.
Этот момент очень легко пропустить, что может привести к значительному потреблению памяти на время работы цикла с большими массивами данных, например при выборке из БД.
Дополнение от пользователя zibada (в кратце):
Важно учесть, что выделение памяти под новый «временный массив» в случае внесения изменений, произойдет единовременно для всей структуры массива, но отдельно для каждого изменяемого элемента. Таким образом, если имеется массив с большим количеством элементов, (но не обязательно с большими значениями), то единовременное потребление памяти при таком копировании будет существенно.
Коварный пример №2
Чуть-чуть изменим код.
Сам код цикла мы никак не меняли, единственное что мы изменили, это увеличили счетчик ссылок на исходный массив, но это в корне поменяло работу цикла:
Bytes diff: 0
Array is set.
Bytes diff: 61940
In FOREACH cycle.
Bytes diff: 61988
In FOREACH cycle.
Bytes diff: 61988
In FOREACH cycle.
Bytes diff: 61988
In FOREACH cycle.
Bytes diff: 61988
Usage right after FOREACH.
Bytes diff: 61940
Array unset.
Bytes diff: 0
Bytes diff: 0
Array is set.
Bytes diff: 61940
In FOREACH cycle.
Bytes diff: 61940
In FOREACH cycle.
Bytes diff: 61940
In FOREACH cycle.
Bytes diff: 61940
In FOREACH cycle.
Bytes diff: 61940
Usage right after FOREACH.
Bytes diff: 61940
Array unset.
Bytes diff: 0
Передача по ссылке или передача через копирование
Рассмотрим случай, «что делать» если требуется передать в метод или функцию (или вернуть из них), какое-либо очень большое значение. Первым очевидным решением обычно рассматривают использование передачи/возвращения по ссылке.
Однако в документации по PHP сказано: Не используйте возврат по ссылке для увеличения производительности. Ядро PHP само занимается оптимизацией.
Попытаемся разобраться в том, что же это за «оптимизация».
Для начала самый простой пример (пока без передачи аргументов):
Start
Bytes diff: 0
String value setted
Bytes diff: 15496
String value unsetted
Bytes diff: 0
В результате получим вывод:
Bytes diff: 0
String value setted
Bytes diff: 30896
String value unsetted
Bytes diff: 0
Данная оптимизация действует для конкретных значений, коими также являются и отдельные значения массива.
Чтобы это лучше понять, взглянем на пример ниже:
Данный пример даст выход:
Bytes diff: 0
String value setted
Bytes diff: 46704
String value unsetted
Bytes diff: 0
Всё выше описанное действует аналогично и для передачи/возврата значений через «оптимизированное копирование» внутрь/из функций и методов. Если внутри метода вы никак не «трогаете» переданное значение, то для него не будет выделена отдельная область памяти (память будет выделена только под имя переменной, чтобы связать ее со значением). Если же вы передаете «через копирование» и изменяете значение внутри метода, то перед попыткой сделать изменение уже будет создана действительная полная копия значения.
Таким образом PHP действительно избавляет от необходимости использовать передачу по ссылке для оптимизации использования памяти. Передача по ссылке имеет практическое значение только если исходное значение требуется изменить с отображением этих изменений извне метода.
echo «Array memory usage example.» ;
$base_memory_usage = memory_get_usage ( ) ;
$base_memory_usage = memory_get_usage ( ) ;
Как видно из примера, в функции не была создана копия массива, несмотря на то, что фактически идет передача значения через копирование. И даже частичная модификация переданного массива не создала полноценную копию, а выделила память только под новые значения.
Исключительно в познавательных целях, стоит обратить внимание на эти два значения:
Примечание:
В PHP5 (в отличие от PHP4), все объекты по-умолчанию передаются по ссылке, хотя по факту, это неполноценная ссылка. См. эту статью.
Краткие выводы
Несомненно приведенные примеры оптимизации использования памяти в PHP лишь «капля в море», однако они описывают самые частые случаи, когда имеет смысл задуматься о том, какой код выбрать чтобы оптимизировать расход памяти и избавить себя от лишней головной боли.
Отдельно стоило бы затронуть механизм расходования и оптимизации памяти при использовании объектов, однако ввиду обилия возможных примеров этот момент требует отдельной статьи. Возможно когда-нибудь.
PS: Можно было бы разбить это на несколько статей, но не вижу в этом смысла, так как подобную информацию лучше всё же хранить «вместе». Полагаю тем, кому данная информация несет практический смысл, так будет удобнее. Тестировалось на PHP 5.3.2 (Ubuntu 32bit), так что ваши значения по выделенным байтам могут отличаться.
UPD
В основной части статьи не был освещен важный момент.
Если есть переменная на которую создана ссылка, то при ее передаче в функцию в качестве аргумента она будет скопирована сразу, то есть не будет применена copy-on-write оптимизация.
Пример:
memory_get_usage
(PHP 4 >= 4.3.2, PHP 5, PHP 7, PHP 8)
memory_get_usage — Возвращает количество памяти, выделенное для PHP
Описание
Возвращает количество памяти в байтах, которое было выделено PHP-скрипту на данный момент.
Список параметров
PHP не отслеживает память, которая выделялась не emalloc()
Возвращаемые значения
Возвращает количество памяти в байтах.
Примеры
Пример #1 Пример использования memory_get_usage()
// Это просто пример, цифры ниже будут
// отличаться в зависимости от вашей системы
Смотрите также
User Contributed Notes 15 notes
To get the memory usage in KB or MB
echo convert ( memory_get_usage ( true )); // 123 kb
?>
Note, that the official IEC-prefix for kilobyte, megabyte and so on are KiB, MiB, TiB and so on.
At first glance this may sound like «What the hell? Everybody knows, that we mean 1024 not 1000 and the difference is not too big, so what?». But in about 10 years, the size of harddisks (and files on them) reaches the petabyte-limit and then the difference between PB and PiB is magnificent.
Better to get used to it now. 🙂
To get the memory usage in KB or MB
function echo_memory_usage () <
$mem_usage = memory_get_usage ( true );
memory_get_usage() is used to retrieve the memory allocated to PHP only (or your running script). But intuitively, many people expect to get the memory usage of the system, based on the name of the function.
So if you need the overall memory usage, following function might be helpful. If retrieves the memory usage either in percent (without the percent sign) or in bytes by returning an array with free and overall memory of your system. Tested with Windows (7) and Linux (on an Raspberry Pi 2):
//
// Extract size (TODO: It seems that (at least) the two values for total and free memory have the unit «kB» always. Is this correct?
//
?>
The function getNiceFileSize() is not required. Just used to shorten size in bytes.