Сила освобождения памяти в PHP

в PHP-программе я последовательно читаю кучу файлов (с file_get_contents ), gzdecode them, json_decode результат, анализирует содержимое, выкидывает большую часть его и хранит около 1% в массиве. К сожалению, с каждой итерацией (я перемещаюсь по массиву, содержащему имена файлов), кажется, потеряна некоторая потеря памяти (в соответствии с memory_get_peak_usage , около 2-10 МБ каждый раз). У меня есть двойной и триплексный мой код, я не храню незавершенные данные в цикле (и необходимые данные почти не превышают около 10 МБ в целом), но я часто переписываю (фактически, строки в массиве). По-видимому, PHP не освобождает память правильно, тем самым используя все больше и больше ОЗУ, пока не достигнет предела. Есть ли способ сделать принудительную сборку мусора? Или, по крайней мере, узнать, где используется память?

Спасибо заранее, Дмитрий

    это связано с фрагментацией памяти.

    Рассмотрим две строки, объединенные одной строкой. Каждый оригинал должен оставаться до тех пор, пока не будет создан выход. Выходной сигнал длиннее, чем вход.
    Поэтому необходимо создать новое распределение для хранения результата такой конкатенации. Исходные строки освобождаются, но они представляют собой небольшие блоки памяти.
    В случае с 'str1' . 'str2' . 'str3' . 'str4' 'str1' . 'str2' . 'str3' . 'str4' 'str1' . 'str2' . 'str3' . 'str4' вас есть несколько темпов, создаваемых на каждом. – и ни один из них не уместился в пространстве, которое было освобождено. Строки, вероятно, не выложены в непрерывной памяти (т. Е. Каждая строка есть, но различные строки не укладываются до конца) из-за других видов использования памяти. Поэтому освобождение строки создает проблему, потому что пространство не может быть эффективно использовано повторно. Таким образом, вы растете с каждым tmp, который вы создаете. И вы никогда ничего не используете.

    Используя массив на основе implode, вы создаете только один результат – точно такую ​​длину, которую вы требуете. Выполнение только 1 дополнительного распределения. Таким образом, он намного эффективнее памяти и не страдает от фрагментации конкатенации. То же самое относится к python. Если вам нужно конкатенировать строки, более 1 конкатенация всегда должна быть основана на массиве:

     ''.join(['str1','str2','str3']) 

    в python

     implode('', array('str1', 'str2', 'str3')) 

    в PHP

    эквиваленты sprintf также прекрасны.

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

    В PHP> = 5.3.0 вы можете вызвать gc_collect_cycles() для принудительного прохождения GC.

    Примечание. Для zend.enable_gc функции zend.enable_gc включенном php.ini или вызове gc_enable() для активации циклического справочного коллектора.

    Нашел решение: это была конкатенация строк. Я генерировал ввод строки за строкой, конкатенируя некоторые переменные (выход представляет собой файл CSV). Тем не менее, PHP, похоже, не освобождает память, используемую для старой копии строки, тем самым эффективно сбивая ОЗУ с неиспользуемыми данными. Переключение на основанный на массиве подход (и его размножение запятыми перед выходом из него в outfile) обошли это поведение.

    По какой-то причине – не очевидно для меня – PHP сообщил об увеличении использования памяти во время вызовов json_decode, которые вводят меня в заблуждение в предположении, что проблема json_decode была проблемой.

    Я обнаружил, что менеджер внутренней памяти PHP чаще всего вызывается после завершения функции. Зная это, я переработал код в цикле:

     while (condition) { // do // cool // stuff } 

    в

     while (condition) { do_cool_stuff(); } function do_cool_stuff() { // do // cool // stuff } 

    РЕДАКТИРОВАТЬ

    Я провел этот быстрый тест и не видел увеличения использования памяти. Это приводит меня к мысли, что утечка не находится в json_decode()

     for($x=0;$x<10000000;$x++) { do_something_cool(); } function do_something_cool() { $json = '{"a":1,"b":2,"c":3,"d":4,"e":5}'; $result = json_decode($json); echo memory_get_peak_usage() . PHP_EOL; } 

    Вызовите memory_get_peak_usage() после каждого утверждения и убедитесь, что вы unset() все, что можете. Если вы выполняете итерацию с помощью foreach() , используйте ссылочную переменную, чтобы избежать копирования оригинала ( foreach () ).

     foreach( $x as &$y) 

    Если PHP действительно утечка памяти, принудительная сборка мусора не будет иметь никакого значения.

    Есть хорошая статья об утечках памяти PHP и их обнаружении в IBM

    У меня была такая же проблема, и я нашел возможное обходное решение.

    СИТУАЦИЯ: Я писал из db-запроса в csv-файлы. Я всегда выделял одну строку $, а затем переназначал ее на следующем шаге. Сброс $ row не помогло; первая строка в строке $ 5 (во избежание фрагментации) не помогла; создание массива $ row-s (загрузка многих строк в него + удаление всего объекта на каждом 5000-м шаге) не помогло; действительно пробовал пару вещей.

    НО.

    Когда я сделал отдельную функцию, открывающую файл, переносит 100 000 строк (достаточно, чтобы не съесть всю память) и закрывает файл, ТОГДА я сделал последующие вызовы этой функции (добавление к существующему файлу), я обнаружил, что для каждый выход функции, PHP удалил мусор. Это была локальная переменная.

    ЗАКЛЮЧЕНИЕ: всякий раз, когда ваша функция выходит, она освобождает все локальные переменные.

    Это правило, насколько я узнал. Однако только одно замечание: когда я попытался заставить мою функцию do_only_a_smaller_subset () получить некоторые переменные по ссылке (а именно объект запроса и указатель файла), сборка мусора не произошла. Теперь, может быть, я что-то недопонимаю, и, возможно, объект запроса (mysqli) течет, ну, я не знаю. Однако, поскольку он был принят ref, очевидно, что он не смог очиститься, так как он существовал как точка выхода малой функции.

    Итак, стоит попробовать! Это спасло мой день, чтобы это выяснить.

    Я собирался сказать, что я не обязательно буду ожидать, что gc_collect_cycles () решит проблему – поскольку предположительно файлы больше не отображаются на zvars. Но вы проверяли, что gc_enable был вызван до загрузки каких-либо файлов?

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

    Я считаю, что одним обходным решением было бы не использовать file_get_contents, а скорее fopen () …. fgets () … fclose (), а не отображение всего файла в память за один раз. Но вам нужно попробовать это подтвердить.

    НТН

    C.

    Недавно была проблема с System_Daemon . Сегодня я изолировал свою проблему до file_get_contents .

    Не могли бы вы попробовать использовать fread ? Я думаю, это может решить вашу проблему. Если да, то, вероятно, настало время сделать bugreport на PHP.