【问题标题】:PHP XML Memory Leak?PHP XML 内存泄漏?
【发布时间】:2016-02-10 08:20:06
【问题描述】:

在我们定期运行的脚本之一中存在严重的内存泄漏,该脚本会迅速清除服务器上的可用内存。尽管进行了许多小时的研究和实验,但我什至无法对此有所了解。

代码如下:

    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 上运行 PHP 5.3.15 和 Apache 2.2.3。我们正在努力更新到所有这些的最新版本,所以沿着升级路径的某个地方,我们可能会发现这个问题已修复。不过,在那之前做这件事会很棒。

【问题讨论】:

  • 试试看$oNodes在你之前有多少引用unsetphp.net/manual/en/features.gc.refcounting-basics.php
  • 假设 apache,请记住每个子节点都有自己独立的内存池,如果您正在并行处理“巨大”xml 文档,则子节点之间不会共享任何 dom 基础结构。您可以将 max_requests_per_child (无论设置如何)设置为低,因此 apache 会更频繁地核对/重新启动子级,这将释放一些持有的内存。
  • @Machavity:我们没有安装 Xdebug。有没有办法在没有它的情况下计算引用?
  • @Marc B:我不确定在这种情况下您所说的“孩子”是什么意思。对不起。在这种情况下,此脚本将运行一次。它不会同时受到两个不同进程的影响。
  • php 不会因为您取消设置变量而运行其垃圾收集器。 GC 运行在计算上非常昂贵,因此 PHP 在必须运行它之前不会运行它,例如记忆越来越紧。所以 memory_get_usage() 并不是一个真正有效的测试。

标签: php dom memory xpath memory-leaks


【解决方案1】:

最近遇到了和你一样的问题。我们需要从 3gb xml 文件中提取数据,并且还注意到服务器内存已达到其极限。有几种方法可以减少内存使用量;

  • 而不是使用导致大量内存使用的 xpath 使用(例如)file_get_contents。然后通过正则表达式进行搜索以找到所需的数据
  • 将 xml 拆分成更小的部分。基本上它重新发明了 xml 文件,但是您可以处理文件的最大大小(因此是内存)

您提到在 30 分钟后释放了一些内存。在 30 分钟内读取 500mb 的 xml 会很慢。我们使用的解决方案是将 3gb xml 文件分成几部分(大约 200 个)。我们的脚本在不到 5 分钟的时间内将所需的数据(大约 70 万条记录)写入我们的数据库。

【讨论】:

    【解决方案2】:

    我们刚刚遇到了与 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) 确保扩展可以重用一些资源。否则,每次运行代码时内存使用量都会增加。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-17
      • 2012-04-17
      • 2012-01-12
      • 2017-01-28
      • 2016-03-30
      • 1970-01-01
      • 2011-10-18
      • 1970-01-01
      相关资源
      最近更新 更多