Web

Как снять ограничение на использование памяти для PHP-скрипта

Ситуация

У меня проблема с PHP-скриптом, который получает следующее сообщение об ошибке:

Fatal error: Out of memory (allocated 359923712) (tried to allocate 72 bytes) in /path/to/piwik/core/DataTable.php on line 969

 Скрипт, который я запускаю: 

/path/to/piwik/misc/cron/archive.sh

Я предполагаю, что цифры — это байты, что означает, что общее количество составляет приблизительно 360 МБ.

Для всех целей я увеличил лимит памяти на сервере намного выше 360 МБ, но именно на этом значении (плюс-минус некоторое количество байт) он постоянно выдает ошибку.

Обратите внимание: этот вопрос не об устранении утечки памяти в скрипте, и не о том, почему сам скрипт использует так много памяти. Скрипт является частью процесса архивирования «Piwik», поэтому я не могу просто исправить утечку памяти и т. д. 

Вопрос

Учитывая, что скрипт пытается использовать более 360 МБ памяти, что я не могу изменить, почему мне кажется невозможным увеличить количество памяти, доступной для php на моем сервере?

Что я пробовал

Увеличивал лимит памяти PHP в файле php.ini:

php -i | grep php.ini

Путь к файлу конфигурации (php.ini) => /usr/local/lib

Загружал файл конфигурации => /usr/local/lib/php.ini

Я отредактировал этот файл, так что директива memory_limit стала выглядеть следующим образом;

memory_limit = -1

Перезапускал Apache и проверял, что новое значение закрепилось:

$ php -i | grep memory_limit

memory_limit => -1 => -1

 

Запускал скрипт и получал ту же ошибку.

Я также пробовал 1G, 768M и т.д., все с тем же результатом (т. е. без изменений).

Снимал ограничения памяти для дочерних процессов Apache.

Я нашел и отредактировал файл httpd.conf, чтобы убедиться, что там нет директивы RLimitMEM.

Затем я использовал WHM's Apache Configuration > Memory Usage Restrictions для создания ограничения, которое, по его утверждению, было на уровне 1000M (и было подтверждено проверкой httpd.conf).

Оба этих способа не привели к изменениям в скрипте, ошибающемся на 360 МБ.

Увеличивал ограничение памяти для каждого процесса в Linux.

Текущие ограничения, установленные в системе:

$ ulimit -m

524288

$ ulimit -v

524288

Я попытался установить оба лимита на неограниченный:

$ ulimit -m unlimited

$ ulimit -v unlimited

$ ulimit -m

unlimited

$ ulimit -v

unlimited

 

И снова это не привело ни к какому решению моей проблемы.

Я также столкнулся со смежной проблемой. Если я устанавливаю ulimit -v 1024000, а затем проверяю его с помощью ulimit -v, я получаю правильное значение "1024000". Если я запущу сценарий снова, он работает намного дольше, но в конце концов выдаст ошибку с тем же пределом памяти, который был достигнут. Если я немедленно проверю ulimit -v, то он вернется к исходному значению «524288». Похоже, что это и есть первопричина проблемы.

Ответ 1

Я взял текущую версию Piwik -- В версии 1.5 строка 969 выглядит следующим образом:

public function addRowsFromSerializedArray( $stringSerialized ) {

    $serialized = unserialize($stringSerialized);

    if($serialized === false) {

        throw new Exception("Ошибка десериализации!");

    }

    $this->addRowsFromArray($serialized);

}

и конкретно $serialized = unserialize($stringSerialized);. Вызов unserialize может занимать невероятно много памяти. 

Как вы отметили, это явно не ошибка в вашем скрипте и является ошибкой – нехватка памяти.

Предложение: В конфигурационном файле:

/.../piwik/config/global.ini.php

Возможно, вам нужно увеличить один из этих лимитов:

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

# максимальное количество строк для любой из таблиц Referers (ключевые слова, поисковые системы, кампании и т. д.)

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

datatable_archiving_maximum_rows_referers = 1000

# максимальное количество строк для любой из подтаблиц Referers (поисковые системы по ключевому слову, ключевое слово по кампании и т .д.)

datatable_archiving_maximum_rows_subtable_referers = 50

# максимальное количество строк для любой из таблиц «Действия» (страницы, загрузки, ссылки)

datatable_archiving_maximum_rows_actions = 500

# максимальное количество строк для страниц в категориях (подстраницы, при нажатии на + для категории страницы)

# примечание: не должно превышать лимит отображения в Piwik_Actions_Controller::ACTIONS_REPORT_ROWS_DISPLAY

# потому что каждый подкаталог не имеет пагинации внизу, поэтому все данные должны быть отображены, если это возможно

datatable_archiving_maximum_rows_subtable_actions = 100

# максимальное количество строк для других таблиц (Провайдеры, конфигурации настроек пользователя)

datatable_archiving_maximum_rows_standard = 500

где я заменил полуколонки на знаки «#» только для того, чтобы сделать автоколор sf читаемым

Вы также можете попробовать добавить:

CMD_TO_CHECK_SETTINGS="$PHP_BIN -i > /tmp/piwik-php-env.out"

$CMD_TO_CHECK_SETTINGS

 

в archive.sh, чтобы определить, есть ли другие настройки, которые отменяют настройки в  файле php.ini.

Ответ 2

 Существует ограничение на размер выполняемой SQL-команды, которое по умолчанию составляет всего 1 МБ. Чтобы увеличить ограничение на размер в MySQL, отредактируйте /etc/my.cnf и установите max_allowed_packet=32M

Убедитесь, что лимит памяти PHP установлен достаточно высоким для каждого процесса, отредактировав /usr/local/lib/php.ini и установив memory_limit = 512M.

Наконец, убедитесь, что все процессы имеют жесткий лимит не менее 1Гб, прежде чем система отключит их, выполнив ulimit -v 1048576 в командной строке.

ulimit -v 1048576 повысит только внутренний лимит. Если жесткий предел недостаточно высок, система автоматически сбросит внутренний предел до общего на процесс.

Чтобы установить общий предел, добавьте переключатель -H:

ulimit -vH 1048576

 а затем увеличьте внутренний предел до этого значения:

ulimit -vS 1048576

Ответ 3

Можете ли вы подтвердить, в каком файле конфигурации вы изменили ограничения памяти? Обратите внимание, что есть два разных конфига: один для PHP, выполняемого через веб-сервер, а второй для PHP, выполняемого через CLI.

~$ ls /etc/php5/

apache2  cli  conf.d

~$ ls /etc/php5/apache2/

conf.d  php.ini

~$ ls /etc/php5/cli/    

conf.d  php.ini

Также, если это выполняется через браузер, в той же директории, где находится сам скрипт, создайте файл temp.php со следующим содержимым:

<?php phpinfo(); ?>

Проверьте, как отображается значение memory_limit при переходе к этому файлу.

Ответ 4

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

 

Я использую Piwik на базовом уровне и не помню, чтобы когда-либо использовал скрипт архивации напрямую. Piwik все еще немного глючит, но последняя версия 1.4 кажется гораздо менее глючной, чем предыдущие. Я не рекомендую возиться с настройками системы или PHP ради того, чтобы бесконечно запускать ошибочный скрипт. Лучше обратиться на форум поддержки piwik.

 

Схожие статьи

PhpMyAdmin: уязвимости, которые могут нанести вред, и как их избежать
Web

PhpMyAdmin: уязвимости, которые могут нанести вред, и как их избежать

Web

Управление индикатором выполнения загрузки в PHP

Web

Предпочтительный метод хранения массивов PHP (json_encode vs serialize)

Web

Как заменить URL-адреса в тексте ссылками HTML