Perl 使用WWW::Mechanize的内存泄漏

Perl 使用WWW::Mechanize的内存泄漏,perl,out-of-memory,www-mechanize,Perl,Out Of Memory,Www Mechanize,我用Perl编写了这个脚本,在运行几分钟后,它出现了一个“内存不足”错误。我看不到任何循环引用,也不知道为什么会发生这种情况 use feature 'say'; use WWW::Mechanize; use HTML::TreeBuilder::XPath; use utf8; $url = "some url"; my $mech = new WWW::Mechanize; $mech->get($url); my $html = HTML::TreeBuil

我用Perl编写了这个脚本,在运行几分钟后,它出现了一个
“内存不足”
错误。我看不到任何循环引用,也不知道为什么会发生这种情况

use feature 'say';
use WWW::Mechanize;
use HTML::TreeBuilder::XPath;
use utf8;

$url = "some url";

my $mech = new WWW::Mechanize;
$mech->get($url);
my $html = HTML::TreeBuilder::XPath->new_from_content($mech->content);
my $html2;

do { 
    for $item ($html->findnodes('//li[@class="dataset-item"]'))
    {
        my $title = $item->findvalue('normalize-space(.//a[2])');
        next unless $title =~ /environmental impact statement/i;        
        my $link = $item->findvalue('.//a[2]/@href');
        $mech->get($link);
        $html2 = HTML::TreeBuilder::XPath->new_from_content($mech->content);
        my @pdflinks = $html2->findvalues('//a[@title="Go to external URL"]/@href');
        my $date = $html2->findvalue('//tr[th="Date Created"]/td');
        for $pdflink (@pdflinks)
        {
            next unless $pdflink =~ /\.pdf$/;
            $mech->get($pdflink);
            $mech->save_content($filename = $mech->response->filename);
            say "Title: $title\nDate: $date\nFilename: $filename\n";
        }
    }
    if ($nextpage = $html->findvalue('//ul[@class="pagination"]/li/a[.="»"]/@href'))
    {
        say "Next Page: $nextpage\n";
        $mech->get("some site" . $nextpage);
        $html = HTML::TreeBuilder::XPath->new_from_content($mech->content);
    }
} while ($nextpage);

say "Completed.";
因为默认情况下,其用户代理在浏览时保留所有历史记录

  • 堆栈深度=>
    $value
设置跟踪所有下载页面的页面堆栈深度。默认值实际上是无限大的堆栈大小。如果堆栈占用了您的内存,则将其设置为较小的数字,例如5或10。将此设置为零意味着Mech将不保留任何历史记录

因此,对象不断增长。通过使用
Devel::Size qw(total_Size)
我跟踪
$mech
的大小,以查看它在每个pdf之后增加了几十kB。剧本显然有很多匹配项;我退出了测试,因为它占用了10%的内存(磁盘上有几十个超过1 Gb的文件)

一种解决方案是为每个
$item
实例化一个新对象。这在原则上是浪费,但实际上不会增加太多开销,同时会限制最大大小

或者重置它,或者限制它的堆栈深度。由于代码似乎根本不需要返回到以前的状态,因此实际上不需要任何堆栈,因此删除它的解决方案非常好

评论

  • 准确地说,脚本中没有“漏洞”;它只需要越来越多的内存

  • 始终做到
    使用严格
    使用警告位于脚本顶部

  • 最好不要使用间接对象语法来实例化对象(
    newpackage
    ),而是使用普通的方法调用(
    Package->new
    ),以避免在某些情况下处理歧义。请参见和中的说明以及和中的故障示例


我这样做似乎很有帮助:
my$mech=WWW::Mechanize->new(堆栈深度=>0)几分钟后,我在磁盘上积累了大约Gb的pdf文件(perl的内存使用一直在剪辑)——我猜:mech对象在不断浏览的过程中保存了所有这些内容。iTesting…@zdim我想我上面的评论已经解决了这个问题。在Mechanize文档中,它建议如果您的内存被占用(关于测试的说明我提到:问题的初始版本提供了URL),它确实需要
使用utf8
,因为
>
字符。使用
使用utf8没有害处即使只使用ASCII字符集中的字符。@ikegami当然,原则上我是在评论一个不必要的(我认为)杂注。@ikegami(奇怪的是,没有杂注,脚本运行得很好…?)