Всем добра.
Имеется простой скрипт, который берет два xml файла и сравнивает их между собой. Точнее сказать, в начале скрипта содержимое файлов импортируется посредством simplexml_load_file, конвертируется в обычный массив, а с двумя массивами и производятся дальнейшие манипуляции.
Получив в один прекрасный момент фатальную ошибку про нехватку памяти, начал изучать проблему и ясности пока нет.
Если (вдруг) имеет значение запускается все на простом конфиге с 4гб оперативной памяти и таким же по объему файлом подкачки, linux mint, apache, php 7.4
Расставил memory_get_usage(true) несколько раз в скрипте, надеясь найти "прожорливую" функцию, зависимости не обнаружил.
Самое для меня непонятное, что пиковое потребление (memory_get_peak_usage(true) поставил в конце скрипта) может выдать 210 МБ (это случилось после запуска скрипта после перезагрузки ОС) После каждого последующего обновления страницы наблюдается странная картина:
1. вариант : скрипт может упасть с не хваткой памяти (в php.ini специально выставил 128Мб после того, как увидел что скрипт может съесть 210МБ),
2. вариант : может выполниться , но при этом показывает постоянно различные мегабайты потребления оперативной памяти.
Код, разумеется не меняется, обновляется страница в бразузере.
Наблюдается некая закономерность. Если подряд выполнять несколько раз, то сначала обычно - фатальная ошибка с нехваткой памяти, потом потребление падает при каждом выполнении, вплоть до 26мб. в пике и 2 мб в теле скрипта.
В скрипте расставлены unset() где это возможно, для очистки памяти.
С чем это связано? Что почитать на эту тему?
улучшает производительность PHP путём сохранения скомпилированного байт-кода скриптов в разделяемой памяти, тем самым избавляя PHP от необходимости загружать и анализировать скрипты при каждом запросе.
Это расширение доступно по умолчанию с PHP 5.5.0
Отключив OPcache вы скорее всего получите стабильный, максимально хреновый результат.
Знаете, я тоже про опкэш подумал, после получения закономерности после нескольких запусков скрипта, и максимального потребления с чистого листа (после перезагрузки)
Попробуйте упростить работу РНР, сделайте преобразование XML в иерархический массив.
Накладные расходы на хранение будут в разы меньше.
Еще меньше если не будете использовать иерархию, а сформируете хеш-массив всех элементов.
xml-структура в путь, далее в хеш
a->b->c = hash(abc) = 01f
$a['01f'] = $value
Реализовать класс для работы с таким массивом, что бы можно было обращаться по "пути xml".
Может показаться излишеством, но памяти будет есть меньше чем работа с simpleXml или DOM - у них много накладных расходов
Если у вас есть файл размеров 10Мб и вы загружаете его в переменную (память 10Мб)
Далее мы начинаем разбирать его как DOM структуру формируя для каждого элемента КЛАСС (1Кб на элемент) ~= 1000строк * 1Кб = 1Мб (и это только структура без данных) (память 21Мб - именно так, ведь вы загрузили в переменную а теперь создали кучу классов в этими данными)
Далее вы начали обрабатывать данные (предположим что вы просто перекладываете их в другой массив) = +10Мб массив (+10кб на хранение хешей ключей) (память 32Мб)
Подставьте свои значений размера первичных данных и операции которые вы делаете - получите понимание куда "течет" ваша память.
paskelas, "Но сначала найти корень моих бед." - каждая объявленная переменная даже равная NULL занимает около 200 байт. Каждый массив = 300байт, Каждый класс - это набор из переменных и массивов
Для того чтобы привести xml к нужному мне виду (ассоциативный массив) я использовал последовательно связку встроенных функций из simplexml_load_file, json_encode json_decode (true), и проблема была именно в них.
Замер (приблизительный) переменной, полученной после simplexml_load_file дал вполне себе вменяемый расход
памяти. Соответственно, проблема была с функциями которые работают с JSON. Сразу же вспомнил, что скрипт всегда падал на json_decode (true), но в тот момент думал, что она последняя в очереди на обработку и ей просто не хватает выделанной памяти.
Далее. Ни unset () ни присваивание null переменным которые получались в ходе работы функций не оказали значимого эффекта.
Проблема устранена после того как исключил json_encode json_decode (true) из кода (получаю массив из xml иным способом, остальной код тот же, файлы те же).
Потребление памяти стабильно и в разумных пределах, скрипт не падает, выполняется за 2-3 секунды (не указал это ранее. но время выполнение скрипта тоже варьировалось от нескольких секунд до минуты.)