- Диагностика утечек памяти. Допустимый размер памяти в # байт.
- Увеличение лимита
- Диагностика утечки:
- Solutions Collecting From Web of «Диагностика утечек памяти. Допустимый размер памяти в # байт.»
- Как определить причину утечки памяти в PHP скрипте?
- Войдите, чтобы написать ответ
- PHP — как отправить письмо с одного EMail на другой, с помощью SMTP+php?
Диагностика утечек памяти. Допустимый размер памяти в # байт.
Я столкнулся с ужасным сообщением об ошибке, возможно, благодаря кропотливой работе, у PHP закончилась нехватка памяти:
Увеличение лимита
Если вы знаете, что делаете и хотите увеличить лимит, см. Memory_limit :
ini_set('memory_limit', '16M'); ini_set('memory_limit', -1); // no limit
Осторожно! Вы можете решить только симптом, а не проблему!
Диагностика утечки:
Сообщение об ошибке указывает на строку с циклом, который, как я полагаю, протекает или ненужно накапливает память. Я напечатал memory_get_usage() в конце каждой итерации и вижу, что число медленно растет, пока оно не достигнет предела:
foreach ($users as $user) < $task = new Task; $task->run($user); unset($task); // Free the variable in an attempt to recover memory print memory_get_usage(true); // increases over time >
сforeach ($users as $user) < $task = new Task; $task->run($user); unset($task); // Free the variable in an attempt to recover memory print memory_get_usage(true); // increases over time >
Для целей этого вопроса давайте предположим, что наихудший код спагетти, который можно вообразить, скрывается в глобальной области где-то в $user или Task .
Какие инструменты, PHP-трюки или отладка voodoo могут помочь мне найти и устранить проблему?
Solutions Collecting From Web of «Диагностика утечек памяти. Допустимый размер памяти в # байт.»
У PHP нет сборщика мусора. Он использует подсчет ссылок для управления памятью. Таким образом, наиболее распространенным источником утечек памяти являются циклические ссылки и глобальные переменные. Боюсь, если вы используете фреймворк, у вас будет много кода, чтобы его найти. Самый простой инструмент – выборочно разместить вызовы на memory_get_usage и сузить его до места, где протекает код. Вы также можете использовать xdebug для создания следа кода. Запустите код с трассировкой выполнения и show_mem_delta .
В php существует несколько возможных точек утечки памяти:
- сам php
- расширение php
- Библиотека php, которую вы используете
- ваш php-код
Трудно найти и исправить первые 3 без глубокой обратной инженерии или знания исходного кода php. Для последнего вы можете использовать бинарный поиск для кода утечки памяти с памятью_get_usage
Я заметил один раз в старом скрипте, что PHP будет поддерживать переменную «as» как в области видимости даже после моего цикла foreach. Например,
foreach($users as $user)< $user->doSomething(); > var_dump($user); // would output the data from the last $user
Я не уверен, что будущие версии PHP исправлены или нет, так как я видел это. Если это так, вы можете unset($user) после doSomething() чтобы удалить его из памяти. YMMV.
Недавно я столкнулся с этой проблемой в приложении, под тем, что я собираю, чтобы быть похожими обстоятельствами. Скрипт, который работает в CLI PHP, который перебирает много итераций. Мой скрипт зависит от нескольких базовых библиотек. Я подозреваю, что причиной является определенная библиотека, и я потратил несколько часов напрасно, пытаясь добавить соответствующие методы деструкции в свои классы безрезультатно. Столкнувшись с длительным процессом преобразования в другую библиотеку (которая может оказаться той же проблемой), я придумал грубую работу по этой проблеме в моем случае.
В моей ситуации, в linux cli, я перебирал кучу пользовательских записей и для каждого из них создавал новый экземпляр нескольких классов, которые я создал. Я решил попробовать создать новые экземпляры классов с помощью PHP-метода exec, чтобы этот процесс выполнялся в «новом потоке». Вот действительно базовый пример того, что я имею в виду:
foreach ($ids as $id) < $lines=array(); exec("php ./path/to/my/classes.php $id", $lines); foreach ($lines as $line) < echo $line."\n"; >//display some output >
Очевидно, что этот подход имеет ограничения, и нужно знать об опасности этого, поскольку было бы легко создать работу на кролике, однако в некоторых редких случаях это могло бы помочь преодолеть трудное место, пока не будет найдено лучшее исправление , как в моем случае.
Я столкнулся с одной и той же проблемой, и я решил заменить foreach на регулярной основе. Я не уверен в специфике, но кажется, что foreach создает копию объекта (или как-то новую ссылку). Используя регулярный цикл, вы напрямую обращаетесь к элементу.
Вот трюк, который мы использовали для определения того, какие сценарии используют большую часть памяти на нашем сервере.
Сохраните следующий фрагмент в файле, например, /usr/local/lib/php/strangecode_log_memory_usage.inc.php :
register_shutdown_function('strangecode_log_memory_usage');
Используйте его, добавив следующее в httpd.conf:
php_admin_value auto_prepend_file /usr/local/lib/php/strangecode_log_memory_usage.inc.php
Затем проанализируйте файл журнала в /var/log/httpd/php_memory_log
Возможно, вам понадобится touch /var/log/httpd/php_memory_log && chmod 666 /var/log/httpd/php_memory_log прежде чем ваш веб-пользователь сможет записать в файл журнала.
Недавно я заметил, что функции лямбда PHP 5.3 оставляют лишнюю память, используемую при их удалении.
for ($i = 0; $i < 1000; $i++) < //$log = new Log; $log = function() < return new Log; >; //unset($log); >
сfor ($i = 0; $i < 1000; $i++) < //$log = new Log; $log = function() < return new Log; >; //unset($log); >
Я не уверен, почему, но, кажется, он принимает дополнительные 250 байт каждый лямбда даже после удаления функции.
Если то, что вы говорите о том, что PHP выполняет только GC после функции, истинно, вы можете обернуть содержимое цикла внутри функции в качестве обходного пути / эксперимента.
Одна огромная проблема, с которой я столкнулся, – это использовать create_function . Как и в лямбда-функциях, он оставляет генерируемое временное имя в памяти.
Другой причиной утечки памяти (в случае Zend Framework) является Zend_Db_Profiler. Убедитесь, что это отключено, если вы запускаете скрипты под Zend Framework. Например, я имел в своем приложении application.ini следующее:
resources.db.profiler.enabled = true resources.db.profiler.class = Zend_Db_Profiler_Firebug
Запустив примерно 25 000 запросов + загрузок до этого, он довел память до хорошего 128 Мб (максимальный максимальный предел памяти).
resources.db.profiler.enabled = false
этого было достаточно, чтобы держать его под 20 Mb
И этот скрипт запускался в CLI, но он создавал экземпляр Zend_Application и запускал Bootstrap, поэтому он использовал конфигурацию разработки.
Это действительно помогло запустить скрипт с профилированием xDebug
Я бы посоветовал вам проверить руководство php или добавить gc_enable() для сбора мусора … Это утечка памяти не влияет на то, как работает ваш код.
PS: php имеет сборщик мусора gc_enable() который не принимает аргументов.
Я немного опаздываю на этот разговор, но я расскажу кое-что, относящееся к Zend Framework.
У меня возникла проблема с утечкой памяти после установки php 5.3.8 (с использованием phpfarm) для работы с ZF-приложением, которое было разработано с помощью php 5.2.9. Я обнаружил, что утечка памяти запускалась в файле httpd.conf Apache, в моем определении виртуального хоста, где сказано SetEnv APPLICATION_ENV «development» . После комментирования этой строки утечки памяти прекратились. Я пытаюсь создать встроенный обходной путь в моем PHP-скрипте (главным образом, определяя его вручную в основном файле index.php).
Я не видел, чтобы это упоминалось здесь, но одна вещь, которая может быть полезна, – это использовать xdebug и xdebug_debug_zval (‘variableName’), чтобы просмотреть их.
Я также могу привести пример расширения php в пути: Z-Ray Zend Server. Если сбор данных разрешен, использование памяти будет накладываться на каждую итерацию так же, как если бы сбор мусора был отключен.
- PHP Неустранимая ошибка: не удается наследовать абстрактную функцию
- Альтернатива CURLOPT_RANGE для захвата определенного раздела
- как обрабатывать ошибку 403 с помощью PHP
- как отправить сообщение facebook другу через график api с помощью Accessstoken
- Лучшая практика для PHP / MySQL Назначение / Система бронирования
- Запись метаданных XMP в jpeg (с помощью PHP) – Использование одиночного или множественного rdf: Блоки описания
- пример создания купона дисконтирования и погашения в php
- Как работает PHP foreach?
- Проблемы с записью в файл с PHP на Ubuntu
- Переопределить функцию php по умолчанию? (Eval)
- Получение PHP для запуска скрипта Python
- Считать конкретные значения в многомерном массиве
- $ не определен в JQuery
- HTTP Request encrypt & decrypt failure с PHP и Objective-C
- Исключить исключение «DOMException» с сообщением «Ошибка запроса иерархии»
Как определить причину утечки памяти в PHP скрипте?
Есть скрипт, который обрабатывает большое количество данных. Сделал unset переменных, которые больше не нужны на каждой итерации, но память продолжает утекать с бешеной скоростью. Есть ли способы определить источник утечки в коде?
Оценить 1 комментарий
Какие библиотеки используются?
Могут давать утечку классы по ресайзу картинок, хмл парсеры итп.
Если юзается phpQuery в цикле, то
phpQuery::unloadDocuments()
Получать записи надо не массивом, а по одной.
И никакая память никуда утекать не будет. ДАЖЕ если не ансетить те переменные, которые и так будут перезаписаны при следующей итерации.
Я заметил существенную разницу в потреблении памяти после того, как сделал unset переменных, которые и так будут перезаписаны при следующей итерации
По поводу массива я огоровился. Конечно, я не записываю findAll в массив.
Однако я перебираю объекты с ссылкой и удаляю элемент из общего списка в конце итерации. Несмотря на то, что переменная будет перезаписана, общий список то увеличивается.
«Не записываю в массив» но «удаляю из общего списка» — как это понимать? В общем, не держать в памяти больше одной записи -самый верный способ избежать «утечки» памяти.
FanatPHP: он использует AR и устаревший фреймворк. Не грузи ему мозг.
AlexChebanenko: как варик разбей работу срипта на последовательности от 100 до 1000 итераций за раз. потом запускай занова, но с оффсетом на выполненные итерации.
память теряется в пхпквери. его уж тожно разбирать не будешь
Максим Гречушников: Даже устаревший фреймворк позволяет не грузить все данные в память. Впрочем, все равно у него текла какая-то дремучая либа.
FanatPHP: ставлю 90% на пхпквери. за yii за 5 лет не видел проблем. на днях в связке с битриксом поймал. битрикс в методе ciblockelement::update херачит память куда то
К сожалению, не могу. Могу сказать лишь то, что это Yii 1, использую ACtiveRecord и phpQuery. Получаю массив записей из бд, каждая запись около 1-4 кб. Обрабатываю текст с помощью phpQuery, делаю записи в бд. Каждую итерацию удаляю все переменные, с которыми работал. Так же удаляю элемент из массива результатов.