PHP XML内存泄漏?

时间:2015-11-09 16:10:20

标签: php dom memory xpath memory-leaks

我们的一个常规运行脚本中存在严重的内存泄漏,这些脚本会快速清除服务器上的可用内存。尽管经过了数小时的研究和实验,我甚至无法对其进行研究。

以下是代码:

    echo '1:'.memory_get_usage()."\n";
ini_set('memory_limit', '1G');
    echo '2:'.memory_get_usage()."\n";

$oXML = new DOMDocument();
    echo '3:'.memory_get_usage()."\n";
$oXML->load('feed.xml'); # 556 MB file
    echo '4:'.memory_get_usage()."\n";

$xpath = new DOMXPath($oXML);
    echo '5:'.memory_get_usage()."\n";
$oNodes = $xpath->query('//feed/item'); # 270,401 items
    echo '6:'.memory_get_usage()."\n";

unset($xpath);
    echo '7:'.memory_get_usage()."\n";
unset($oNodes);
    echo '8:'.memory_get_usage()."\n";
unset($oXML);
    echo '9:'.memory_get_usage()."\n";

这是输出:

1:679016
2:679320
3:680128
4:680568
5:681304
6:150852408
7:150851840
8:34169968
9:34169448

如您所见,当我们使用xpath将节点加载到对象中时,内存使用量从681,304跳到150,852,408。我对此并不十分关注。

我的问题是,即使在销毁$ oNodes对象后,我们仍然停留在内存使用量为34,169,968。

但是真正的问题是PHP显示的内存使用量只是脚本占用的总内存的一小部分。直接从服务器上的命令行使用free -m,我们将使用3,295 MB内存到5,226 MB - ,它永远不会再回落。每次运行此脚本时,我们都会丢失2 GB的内存,而且我完全不知道为什么或如何修复它。

我尝试使用SimpleXML,但结果基本相同。我也研究了这三个主题,但没有找到任何有帮助的内容:

XML xpath search and array looping with php, memory issue

DOMDocument / Xpath leaking memory during long command line process - any way to deconstruct this class

DOMDocument PHP Memory Leak

我希望这很简单,我只是忽略了。

更新11/10: 显示内存最终被释放。我注意到在超过30分钟之后,突然又出现了一大块空地。但显然,最近这种情况还不够快,以防止服务器耗尽内存并锁定。

对于它的价值,我们在Red Hat 5.11上使用Apache 2.2.3运行PHP 5.3.15。我们正在努力更新所有这些版本的最新版本,因此在升级路径的某个地方,我们可能会发现这一点已经修复。不过,在此之前做这件事会很棒。

1 个答案:

答案 0 :(得分:0)

我们刚刚遇到了PHPDocxPro(使用DomDocument)的类似问题,并向他们提交了一个补丁,至少可以改善该问题。 get_memory_usage()报告的内存使用从未增加,好像PHP根本不知道分配情况一样。通过topps观看执行过程时报告的内存是我们更加关注的。

// ps reports X memory usage
var $foo = (new DomDocument())->loadXML(getSomeXML());
// ps reports X + Y memory usage
var $foo = (new DomDocument())->loadXML(getSomeXML());
// ps reports X + ~2Y memory usage
var $foo = (new DomDocument())->loadXML(getSomeXML());
// ps reports X + ~3Y memory usage

在每次后续调用之前添加unset()...

// ps reports X memory usage
var $foo = (new DomDocument())->loadXML(getSomeXML());
// ps reports X + Y memory usage
unset($foo);
var $foo = (new DomDocument())->loadXML(getSomeXML());
// ps reports X + ~Y memory usage
unset($foo);
var $foo = (new DomDocument())->loadXML(getSomeXML());
// ps reports X + ~Y memory usage

我没有深入研究扩展代码以了解发生了什么,但是我的猜测是他们在不使用PHP分配的情况下分配内存,因此,get_memory_usage()并没有被计入堆中考虑。尽管如此,似乎确实有一些引用计数来确定是否可以释放内存。后续调用之前的unset($foo)确保扩展名可以重用某些资源。否则,每次运行代码时内存使用量都会增加。