2

我正在尝试使用 PHP Simple HTML Dom Parser 来解析来自某些站点的一些信息。什么和在哪里都无所谓。但它似乎存在一些巨大的内存问题。我设法将 html 代码削减到只有 6kB,但是找到一些元素并将它们保存到数据库的脚本甚至需要 700MB 的内存和超过 1GB 的虚拟内存!我在某处读到我应该使用 ->clear() 来释放一些内存,但似乎情况并非如此。

我使用str_get_html()一次和五次->find()将结果分配给变量。

$main_html = str_get_html($main_site);
$x = $main_html->find(...);
$y = $main_html->find(...);

etc.

例如,我在使用 $y 后尝试使用,但即使确实存在并且是真的$y->clear(),我也会收到错误消息。甚至返回。_PHP Fatal error: Call to a member function clear() on a non-object$yif($y)foreach($y) echo $y->plaintextplaintext$y

从 htop:

PID USER     PRI  NI  VIRT   RES   SHR S CPU% MEM%   TIME+  Command
8839 username    20   0 1068M  638M   268 R 23.0  8.0  0:08.41 php myscript.php

怎么了?

简单测试:

echo "(MEM:".memory_get_usage()."->";
$product = $p->find('a',0)->href;
echo memory_get_usage()."->";
unset($product);
$p->clear();
unset($p);
echo memory_get_usage().")";

结果是:

(MEM:11865648->11866192->11865936)

更具可读性的形式:

11865648->
11866192-> (+544 in total)
11865936 (+288 in total)

当然我不能像它所说的那样使用 $product->clear()PHP Fatal error: Call to a member function clear() on a non-object

4

2 回答 2

9

似乎在使用或类似的函数时会出现一些内存问题,这些函数会多次str_html_get创建对象而没有清除和破坏前一个对象。simple_html_dom特别是在使用 ->find 创建simple_html_dom_node对象数组时。甚至作者网站上的常见问题解答都说要simple_html_dom在创建新对象之前清除和销毁以前的对象,但有时如果没有额外的代码和内存就无法完成。

这就是我创建此函数的原因,以从内存中删除所有 PHP Simple HTML Dom Parser 跟踪:

function clean_all(&$items,$leave = ''){
    foreach($items as $id => $item){
        if($leave && ((!is_array($leave) && $id == $leave) || (is_array($leave) && in_array($id,$leave)))) continue;
        if($id != 'GLOBALS'){
            if(is_object($item) && ((get_class($item) == 'simple_html_dom') || (get_class($item) == 'simple_html_dom_node'))){
                $items[$id]->clear();
                unset($items[$id]);
            }else if(is_array($item)){
                $first = array_shift($item);
                if(is_object($first) && ((get_class($first) == 'simple_html_dom') || (get_class($first) == 'simple_html_dom_node'))){
                    unset($items[$id]);
                }
                unset($first);
            }
        }
    }
}

用法:

从内存中清除 PHP Simple HTML Dom Parser 的所有痕迹:clean_all($GLOBALS);

从内存中清除 PHP Simple HTML Dom Parser 的所有痕迹,$myobj 除外:clean_all($GLOBALS,'myobj');

从内存中清除 PHP Simple HTML Dom Parser 的所有痕迹,除了对象列表 ($myobj1,$myobj2...):clean_all($GLOBALS,array('myobj1','myobj2'));

希望它也能帮助其他人。


通常我在使用 str_to_html() 两次时使用它,例如:

$site=file_get_contents('http://google.com');
$site_html=str_get_html($site);
foreach($site->find('a') as $a){
   $site2=file_get_contents($a->href);
   $site2_html=str_get_html($site2);
   echo $site2->find('p',0)->plaintext;
}
clean_all($_GLOBALS);

在这个例子中我不能$site_html->clear()before foreach{},因为foreachthen 会失败。并且因为在没有清除以前的情况下调用多个str_get_html(),冗余依赖关系被破坏并在所有留下内存泄漏之后清除它。这就是为什么我的函数必须在定义的变量中搜索 simple_html_dom 对象并手动清除它们的原因。

在我的情况下,我在 foreach 内部分叉,经过几步之后,主 php 脚本使用了 100MB 的内存。并且在分叉几次时,它一直在增加,并最终将我的服务器杀死。差不多吧。当然,当 PHP 脚本结束时,它会释放内存。但是当使用 8GB 内存时,需要很长时间才能结束。

于 2013-08-07T05:29:38.857 回答
2

我相信你需要clear()打电话$main_html

从文档...

问:这个脚本严重的内存泄漏... 运行完成后,它没有从内存中正确清理 dom 对象..

A: 由于 PHP5 循环引用内存泄漏,创建 DOM 对象后,如果多次$dom->clear()调用必须调用释放内存。file_get_dom()

例子:

$html = file_get_html(...); 
// do something... 
$html->clear(); 
unset($html);
于 2013-08-06T20:52:39.983 回答