Подтвердить что ты не робот

Как исправить утечку памяти в PHP

В моем приложении PHP есть импорт script, который может импортировать записи.

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

После того, как импортировано около 2500 записей, PHP умирает, заявив, что он преодолел свой предел памяти (132 МБ или около того).

Файл CSV сам по себе является всего лишь несколькими мегабайтами - другая обработка, которая происходит, содержит множество сравнений строк, различий и т.д. У меня на нем огромное количество кода, и было бы сложно найти "самый маленький воспроизводящий образец".

Каким образом можно найти и устранить такую ​​проблему?

Причина найденной проблемы

У меня есть класс отладки, который регистрирует все мои запросы к базе данных во время выполнения. Так что эти строки SQL, длиной около 30 КБ, остались в памяти. Я понимаю, что это не подходит для сценариев, предназначенных для работы в течение длительного времени.

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

4b9b3361

Ответ 1

Это поможет взглянуть на код, но если вы хотите отладить его самостоятельно, посмотрите Xdebug, это 'll помочь профилировать ваше приложение.

Конечно, в зависимости от того, что вы делаете, возможно, он накапливает некоторую память, хотя 132MB кажется уже высоким для 2500 записей. Конечно, вы можете настроить ограничение памяти в php.ini, если это необходимо.

Насколько велика CSV файл, который вы читаете? И какие объекты и вид обработки вы делаете с ним?

Ответ 2

Если вы действительно подозреваете, что в вашем script есть только один или два утечки памяти, которые приводят к его сбою, вы должны предпринять следующие шаги:

  • Измените memory_limit на что-то маленькое, например, 500KB
  • Прокомментируйте все, кроме одного из шагов обработки, которые применяются к каждой строке.
  • Запустите ограниченную обработку по всему CSV файлу и посмотрите, может ли он завершиться.
  • Постепенно добавьте еще несколько шагов и посмотрите, не влияет ли использование памяти.

Пример:

ini_set('memory_limit', 1024 * 500);
$fp = fopen("test.csv", 'r');
while($row = fgetcsv($fp)) {
    validate_row($row);         // step 1: validate
    // add these back in one by one and keep an eye on memory usage
    //calculate_fizz($row);     // step 2: fizz
    //calculate_buzz($row);     // step 3: buzz
    //triangulate($row);        // step 4: triangulate
}
echo "Memory used: ", memory_get_peak_usage(), "\n";

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

Ответ 3

Это зависит от того, как вы очищаете переменные после выполнения с ними.

Похоже, вы закончили запись, но вы все еще храните информацию где-то. Используйте unset(), чтобы очистить переменные, если есть сомнения.

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

BTW, создавая наименьший образец кода, который будет воспроизводить проблему, - это отличный метод отладки, потому что он заставляет вас снова пройти код, с осторожностью.

Ответ 4

вы можете попробовать локальную установку php5.3 и вызвать http://www.php.net/manual/en/function.gc-collect-cycles.php.

gc_collect_cycles - Заставляет собирать любые существующие циклы мусора

если ситуация улучшится, вы, по крайней мере, проверили (на) проблему (ы).

Ответ 5

Как вы читаете файл? Если вы используете fread/filegetcontents или другие подобные функции, тогда вы собираетесь использовать весь размер файла (или сколько бы вы загрузили его с помощью fread) в памяти, поскольку весь файл загружается во время разговора. Однако если вы используете fgetcsv, если будете читать только одну строку за раз в зависимости от длины строки, это может быть значительно проще на вашем память.

Также убедитесь, что вы повторно используете как можно больше переменных для каждого цикла. Убедитесь, что в них нет массива с большими объемами данных.

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

$fh = fopen(...);
while(true)
{
//...
}
fclose($fh);

Вы действительно не хотите этого делать:

while(true)
{
$fh = fopen(...);
//...
fclose($fh);
}

И, как и другие, сказали, что будет сложно сказать, не видя кода.

Ответ 6

Сложно сказать причину, не видя никакого кода. Однако типичной проблемой являются рекурсивные ссылки, т.е. объект A указывает на объект B и наоборот, что может привести к сбою GC.

Я не знаю, как вы сейчас обрабатываете файл, но вы можете попытаться только прочитать файл по одной строке за раз. Если вы сразу прочитаете весь файл, он может потреблять больше памяти.

Это на самом деле одна из причин, по которым я часто предпочитаю Python для задач пакетной обработки.

Ответ 8

У меня была такая же проблема, и это было связано также с профилированием базы данных (Zend_Db_Profiler_Firebug). В моем случае это утечка 1 мб в минуту. этот script должен был работать в течение нескольких дней, поэтому он сработает через несколько часов.