3

在每篇关于 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 MB

65.98 MB

65.98 MB

所以我的问题是 - 有没有其他人注意到它,对此有什么解释,因为我在网络上的任何地方都找不到它的解释 - 甚至没有关于它的确认?

4

3 回答 3

5

PHP 的内存管理功能非常复杂,准确测量特定高级代码的影响是非常困难的。Julien Pauli 在 PHP UK Conference 上对此进行了很好的(非常技术性的)演讲,这里有一段视频

有几个可能的原因memory_get_usage可能会骗你:

  • 首先,memory_get_usage采用可选参数$real_usage,它区分分配的内存量和使用量- 内存管理器一次分配一个块的内存,因此它通常会从操作系统中获得比实际使用更多的内存。当需要更多时,已经占用的内存被用完,这意味着不需要再分配。在这种情况下进行的测试表明这与这里无关。
  • 更一般地说,在运行 PHP 的底层 C 代码中分配内存有不同的方法。由于 SimpleXML 的大部分工作不是在 Zend 引擎中完成,而是在名为 libxml2 的第三方库中完成,因此内存分配将在那里完成,而不是在 PHP 特定的分配例程中完成,例如,追加到 PHP 字符串。

我从 Julien Pauli 的幻灯片中获取了以下函数,它查看了正在运行的 PHP 进程的 Linux 内核视图,并找到代表“驻留集大小”的行 - 实际分配的物理内存量,而不是数量该进程已要求保留:

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

在示例代码中添加对 this(以及对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
于 2013-09-21T15:21:29.500 回答
0

如果我执行脚本,我会得到完全相同的结果。一种解释可能是您不使用 XML 对象,因此甚至没有完全解析 xml 字符串。当您修改脚本以便将数据发送到浏览器print_r($products_sxml);时,调用后内存使用率要高得多。您应该明显减少 xml 中的产品数量。

于 2013-09-21T14:06:13.867 回答
0

SimpleXML 将 XML 树存储在 get_memory_usage 函数不包含的外部资源中。

于 2014-05-15T07:48:46.620 回答