PHP SimpleXML большой файл без дополнительного использования памяти

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

<?php error_reporting(E_ALL); ini_set("display_errors", 1); print "OS: " . php_uname() . "\n"; print "PHP version: " . phpversion() . "\n"; print round(memory_get_usage() / 1024 / 1024, 2) . " Mb\n"; $large_xml = '<?xml version="1.0" encoding="UTF-8"?><catalog><products>'; for ($i = 0; $i < 500000; $i++) { $large_xml .= "<product><id>{$i}</id><name>Product Name {$i}</name><description>Some Description {$i}</description><price>{$i}</price></product>\n"; } $large_xml .= "</products></catalog>"; print round(memory_get_usage() / 1024 / 1024, 2) . " Mb\n"; $products_sxml = simplexml_load_string($large_xml); print round(memory_get_usage() / 1024 / 1024, 2) . " Mb\n"; ?> 

Я тестировал этот скрипт на сервере Linux, версию PHP: 5.3.8, а результат был:

ОС: Linux 2.6.32-5-amd64 # 1 SMP Mon Feb 25 00:26:11 UTC 2013 x86_64

Версия PHP: 5.3.8

0,6 Мб

65.98 Мб

65.98 Мб

Поэтому мой вопрос: кто-нибудь еще заметил это и что может быть для этого объяснением, потому что я не мог найти нигде в Интернете его объяснение – даже не подтверждение об этом?

Функциональность управления памятью PHP довольно сложна, и точное измерение воздействия конкретного куска высокоуровневого кода довольно сложно. Об этом был очень хороший (очень технический) разговор Жюльена Паули на конференции PHP UK, видео которого доступно здесь .

Существует несколько возможных причин, по которым memory_get_usage может лгать вам:

  • Во-первых, memory_get_usage принимает необязательный параметр $real_usage , который различает объем выделенной памяти и используемую сумму – менеджер памяти распределяет память по блоку за раз, поэтому он часто требовал больше от ОС, чем на самом деле использовать. Поскольку требуется больше, уже заявленная память израсходована, что означает, что больше не нужно выделять. Тестирование в этом случае говорит о том, что это не имеет значения.
  • В более общем плане, существуют различные способы выделения памяти в базовом C-коде, который запускает PHP. Поскольку большая часть работы SimpleXML выполняется не в Zend Engine, а в сторонней библиотеке с именем libxml2, выделение памяти будет выполняться там, а не в процедурах распределения PHP, которые будут использоваться, когда, скажем, добавление к строке PHP.

Я воспользовался следующей функцией слайдов Жюльена Паули, которая рассматривает представление ядра Linux о запущенном PHP-процессе и находит строку, которая представляет «Размер резидентного набора» – объем физической памяти, который был фактически выделен, а не сумма процесс попросил зарезервировать:

 function heap() { return shell_exec(sprintf('grep "VmRSS:" /proc/%s/status', getmypid())); } 

Добавляя вызов к этому (а также get_memory_usage(true) ) в вашем примере кода, я получил следующий вывод, показывающий значительное распределение памяти «кучи» при анализе XML:

 OS: Linux pink-marmalade 3.8.0-29-generic #42~precise1-Ubuntu SMP Wed Aug 14 16:19:23 UTC 2013 x86_64 PHP version: 5.3.10-1ubuntu3.8 memory_get_usage(): 0.61 Mb memory_get_usage(true): 0.75 Mb Heap: VmRSS: 6956 kB memory_get_usage(): 65.99 Mb memory_get_usage(true): 66.25 Mb Heap: VmRSS: 74348 kB memory_get_usage(): 65.99 Mb memory_get_usage(true): 66.25 Mb Heap: VmRSS: 761836 kB 

Если я выполню скрипт, то получаю точно такие же результаты. Одно объяснение может заключаться в том, что вы не используете XML-объект, поэтому строка xml даже не разбирается полностью. При изменении сценария, чтобы данные были отправлены в браузер print_r($products_sxml); использование памяти намного выше после вызова. Вы должны уменьшить количество продуктов в xml, очевидно.

SimpleXML хранит дерево XML в внешнем ресурсе, который не включен функцией get_memory_usage.