Я думаю, что нашел проблему, которая, кажется, создает утечку памяти в Apache / PHP, когда символы Юникода в качестве разделителя или иногда в любом месте регулярного выражения с preg_match
а также preg_replace
, Вполне возможно, что это происходит в более preg_*
методы.
Создать новый файл PHP test.php
со следующим содержанием:
<?php
preg_match( '°test°i', 'test', $matches );
Создать новый файл 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 терабайт.
Выполнение сценариев 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 настроен
У меня похожая ошибка: 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. Это на самом деле сообщается исправлено, но в моем случае это не так.
На самом деле очень трудно получить какие-либо правила в этом поведении, но вот что я вижу.
После выполнения, если бы я использовал что-то вроде этого кода для получения результатов:
$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.
Других решений пока нет …