apache — PHP пытается выделить 127 ТБ памяти и утечки памяти с помощью preg_match и preg_replace

Я думаю, что нашел проблему, которая, кажется, создает утечку памяти в Apache / PHP, когда символы Юникода в качестве разделителя или иногда в любом месте регулярного выражения с preg_match а также preg_replace, Вполне возможно, что это происходит в более preg_* методы.

Тестовый пример 1

Создать новый файл PHP test.php со следующим содержанием:

<?php
preg_match( '°test°i', 'test', $matches );

Контрольный пример 2

Создать новый файл PHP test.php со следующим содержанием:

<?php
preg_match( '°', 'test', $matches );

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

Результат

Загрузив файл на веб-сервер с Apache 2.4.10 (Debian) а также PHP 5.6.0-1+b1, запустите его из вашего любимого браузера. Ожидайте увидеть пустую страницу или сообщение о том, что «неверный ответ» или «эта страница не может быть загружена».

Это приведет к следующим двум строкам в вашем файле Apache error.log (обычно /var/log/error.log):

[Mon Dec 15 10:31:09.941622 2014] [:error] [pid 6292] [client ###.###.###.###:64413] PHP Warning:  preg_match():  in /path/to/test.php on line 2
[Mon Dec 15 10:31:09.941796 2014] [:error] [pid 6292] [client ###.###.###.###:64413] PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 139979759686648 bytes) in Unknown on line 0

Обратите внимание, что количество байтов, которое PHP пытался выделить, составляет чуть более 127 терабайт.

Вам нужно перезагрузить Apache сейчас

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

Class MyClass not found in file MyExtendingClass.php on Line 3

И файл MyExtendingClass.php будет выглядеть так:

<?php
class MyExtendingClass extends MyClass
{
}

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

Очевидно, не используйте символы Юникода в регулярных выражениях. Но почему PHP теряет память при использовании определенных символов Юникода? Есть ли объяснение этому поведению? Я хотел бы знать, почему PHP считает, что он должен выделять такое огромное количество байтов.

Системная информация

Apache / 2.4.10 (Debian) PHP / 5.6.0-1 + b1 OpenSSL / 1.0.1i настроен

3

Решение

У меня похожая ошибка: PHP пытается загрузить в ОЗУ что-то чуть больше 127 ТБ, но со мной это происходит ПОСЛЕ завершения скрипта.

PHP Fatal error:  Allowed memory size of 134217728 bytes exhausted (tried to allocate 140683487706824 bytes) in Unknown on line 0

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

Моя проблема, скорее всего, связана с использованием комбинации dblib + freetds для подключения к удаленному серверу MSSQL из Debian, который является известная ошибка # 64511. Это на самом деле сообщается исправлено, но в моем случае это не так.

На самом деле очень трудно получить какие-либо правила в этом поведении, но вот что я вижу.

  • Подключаюсь к удаленному MSSQL
  • выполнить запрос (который на самом деле состоит из вызова хранимой процедуры, а затем сразу после этого выполняет запрос SELECT)

После выполнения, если бы я использовал что-то вроде этого кода для получения результатов:

$sprocResultSet = $PDOquery->fetchAll(PDO::FETCH_ASSOC);
$PDOquery->nextRowset();
$sprocExitCode = $PDOquery->fetchAll(PDO::FETCH_ASSOC);

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

Когда вместо указания стиля извлечения внутри fetchAll () я использовал setFetchMode (), как здесь:

$PDOquery->setFetchMode(PDO::FETCH_ASSOC);
$sprocResultSet = $PDOquery->fetchAll();
$PDOquery->nextRowset();
$sprocExitCode = $PDOquery->fetchAll();

тогда я никогда не замечал такой же ошибки при распределении оперативной памяти

Я пытался закрыть курсоры и обнулить переменную $ PDOquery, или просто разрешить ее автоматическое закрытие в конце скрипта — ни один не помог. Также, возможно, важно упомянуть — и sproc, и дополнительный запрос SELECT после того, как он возвращает только одну строку данных, так что определенно нет большого набора возвращаемых результатов.

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

И просто две вещи, чтобы добавить. Во-первых, в вышеприведенном коде используется PDR nextResultSet (), о чем также сообщается как об ошибке, вызывающей проблему с памятью: nextRowset вызывает повреждение памяти.
Во-вторых, метод PDO fetch () НЕ используется, потому что всякий раз, когда он используется, он умирает с сообщением об ошибке DBLIB.

1

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

Других решений пока нет …

По вопросам рекламы [email protected]