在每篇关于 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 周一 2月 25 00:26:11 UTC 2013 x86_64
PHP 版本: 5.3.8
0.6兆字节
65.98兆字节
65.98兆字节
所以我的问题是 - 其他人是否注意到了它,对此有什么解释,因为我在网上任何地方都找不到它的解释 - 甚至没有关于它的确认?
内存管理功能非常复杂,准确测量特定高级代码段的影响是相当困难的。Julien Pauli 在 PHP UK 会议上对此进行了很好的(非常技术性的)演讲,其视频可在此处获得。
memory_get_usage
可能对你撒谎有几个可能的原因:
- 首先,
memory_get_usage
采用可选参数$real_usage
,它区分分配的内存量和使用量 - 内存管理器一次分配一个块的内存,因此它通常会从操作系统中索取比实际使用的更多。当需要更多内存时,已经声明的内存将被用完,这意味着不需要分配更多内存。在这种情况下的测试表明这在这里无关紧要。 - 更一般地说,在运行 PHP 的底层 C 代码中有不同的分配内存的方法。由于 SimpleXML 的大部分工作不是在 Zend Engine 中完成的,而是在一个名为 libxml2 的第三方库中完成的,因此内存分配将在那里完成,而不是在特定于 PHP 的分配例程中完成,例如,在附加到 PHP 字符串时使用。
我从Julien Pauli的幻灯片中获取了以下函数,该函数查看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函数不包含该资源。