使用 WWW::Mechanize 执行重复查询时内存泄漏
Memory leak when performing repeated queries using WWW::Mechanize
我试图在我的程序中查找内存泄漏。我找到了泄漏的源头,但我无法修复它。
该程序读取与维基百科上找到的每条染色体相连的每个基因页面 Genes by human chromosome
程序在每个基因页面上提取我感兴趣的信息,移动到下一个基因页面等等。
一旦到达当前染色体基因列表的末尾,它就会移动到下一条染色体,直到它遍历每一页。
大约 2-3 周前,该程序在我的计算机上运行。从那以后就开始出现这个问题了。
我一直在使用 top
进行监控,随着程序的运行,内存使用量明显增加,直到达到临界点并且我的计算机崩溃。
根据要求,我提供了可以编译的代码。我从 21 号染色体开始,因为它的页数最少,因此通读所需的时间最少。在此代码片段中,内存使用量仍在递增,所以希望这就足够了!此外,eval 语句在那里,因为查询维基百科 API 有时 returns 什么都没有,而不是预期的 JSON。 eval 函数允许我在不让程序死掉的情况下解决这个问题
我的(更新的)代码
#!/usr/bin/env perl -w
use common::sense;
use WWW::Mechanize;
use URI;
use HTTP::Request;
use Cpanel::JSON::XS qw(decode_json);
my ( $self, $registry ) = @_;
my $mech = WWW::Mechanize->new();
my $root = URI->new("http://en.wikipedia.org/w/api.php");
my $url = $root->clone();
for my $i ( 21 .. 25 ) {
my $chrom = $i;
if ( $chrom == 23 ) {
$chrom = "M";
}
elsif ( $chrom == 24 ) {
$chrom = "Y";
}
elsif ( $chrom == 25 ) {
$chrom = "X";
}
print "Hi!\n The chromosome is $chrom\n";
my $query = {
action => 'query',
format => 'json',
list => 'categorymembers',
cmtitle => "Category:Genes on human chromosome $chrom",
cmlimit => 'max',
cmcontinue => ''
};
$url->query_form($query);
my @gene_pages = ();
eval {
while ( my $response = $mech->get($url) ) {
my $perl_scalar = decode_json( $response->decoded_content() )
; #J Source of malformed JSON string error
push @gene_pages, @{ $perl_scalar->{query}->{categorymembers} };
my $count = @gene_pages;
# Adapted code to new format for continuing queries
if ( $perl_scalar->{continue} ) {
$query->{cmcontinue} = $perl_scalar->{continue}->{cmcontinue};
$url->query_form($query);
}
else {
last;
}
}
};
if ( $@ =~ /malformed/ ) {
redo;
}
my $gene_count = 0;
eval {
foreach my $gene_page (@gene_pages) {
$gene_count++;
my $url = $root->clone();
my $query = {
action => 'query',
prop => 'revisions',
format => 'json',
rvprop => 'content|tags|timestamp',
pageids => $gene_page->{pageid}
};
$url->query_form($query);
# $log->debugf("Requesting: %s", $url->as_string());
my $response = $mech->get($url);
my $content = $response->decoded_content();
my $perl_scalar = decode_json( $response->decoded_content() )
; #J Source of malformed JSON string error
if ( $gene_count % 10 == 0 ) {
print "$gene_count gene pages complete\n";
}
}
};
print "There were $gene_count genes found for chromosome $chrom\n";
}
这段代码的组成部分要大得多,但我将其排除在外,因为我知道这是问题根源所在的区域。
使用WWW::Mechanize
的while
循环部分
my $response = $mech->get($url)
与内存泄漏有关。
如果我删除该组件并且 运行 内存使用的程序保持大致相同然后将其添加回去显示内存再次增加。
Perl 版本:5.24.1
系统:Ubuntu16.04
编辑:@Borodin 感谢您如此详尽的回复!不幸的是,我仍然注意到我的计算机上存在内存泄漏,这让我想知道是否还有更大的问题。
它仍然会逐渐占用内存,目前我的电脑还可以,但是当我 运行 包含一些网络抓取的完整程序时,我不知道我的电脑是否足够。
关于一个可能相关的说明 -- 我的计算机有一个奇怪的问题,它有时无法完全下载文件(尽管下载已完成,但文件仍未 运行 分类)。当我 运行ning 你的程序时,我经常遇到这个错误:
** 解析 JSON 字符串时字符串意外结束,字符偏移量为 5506(在“(字符串结束)”之前)**
这似乎与我遇到的问题有关,我想知道这是否会导致内存泄漏问题?
您不使用 WWW::Mechanize
中 LWP::UserAgent
未提供的任何部分,因此我建议您推迟到后者
这是一些工作代码,其功能与您自己的程序几乎相同。它对我来说没有任何内存泄漏
请询问您是否需要任何解释;整个程序内容太多
#!/usr/bin/env perl
use strict;
use warnings 'all';
use URI;
use URI::QueryParam;
use LWP;
use JSON::XS qw(decode_json);
STDOUT->autoflush;
my $api_root = URI->new( 'http://en.wikipedia.org/w/api.php' );
my @chromosomes = ( 1 .. 22, qw/ M Y X/ );
my $ua = LWP::UserAgent->new;
for my $chrom ( @chromosomes[20..$#chromosomes] ) {
#print "The chromosome is $chrom\n";
my $query = {
action => 'query',
format => 'json',
list => 'categorymembers',
cmtitle => "Category:Genes on human chromosome $chrom",
cmlimit => 'max',
};
my $url = $api_root->clone;
$url->query_form( $query );
my @gene_pages;
while () {
my $resp = $ua->get( $url );
die $resp->status_line unless $resp->is_success;
# J Source of malformed JSON string error
my $data = decode_json( $resp->decoded_content );
my $query = $data->{query};
my $continue = $data->{continue};
push @gene_pages, @{ $query->{categorymembers} };
# Adapted code to new format for continuing queries
last unless $continue;
$url->query_param( cmcontinue => $continue->{cmcontinue} );
}
printf "Processing %d gene pages for chromosome %s\n",
scalar @gene_pages,
$chrom;
my $gene_count;
for my $gene_page ( @gene_pages ) {
++$gene_count;
my $url = $api_root->clone;
my $query = {
action => 'query',
prop => 'revisions',
format => 'json',
rvprop => 'content|tags|timestamp',
pageids => $gene_page->{pageid}
};
$url->query_form( $query );
# print "Requesting: $url\n";
my $resp = $ua->get( $url );
die $resp->status_line unless $resp->is_success;
my $content = $resp->decoded_content;
my $data = decode_json( $content ); # J Source of malformed JSON string error
print "$gene_count gene pages complete\n" unless $gene_count % 10;
}
print "There were $gene_count genes found for chromosome $chrom\n";
}
我试图在我的程序中查找内存泄漏。我找到了泄漏的源头,但我无法修复它。
该程序读取与维基百科上找到的每条染色体相连的每个基因页面 Genes by human chromosome
程序在每个基因页面上提取我感兴趣的信息,移动到下一个基因页面等等。
一旦到达当前染色体基因列表的末尾,它就会移动到下一条染色体,直到它遍历每一页。
大约 2-3 周前,该程序在我的计算机上运行。从那以后就开始出现这个问题了。
我一直在使用 top
进行监控,随着程序的运行,内存使用量明显增加,直到达到临界点并且我的计算机崩溃。
根据要求,我提供了可以编译的代码。我从 21 号染色体开始,因为它的页数最少,因此通读所需的时间最少。在此代码片段中,内存使用量仍在递增,所以希望这就足够了!此外,eval 语句在那里,因为查询维基百科 API 有时 returns 什么都没有,而不是预期的 JSON。 eval 函数允许我在不让程序死掉的情况下解决这个问题
我的(更新的)代码
#!/usr/bin/env perl -w
use common::sense;
use WWW::Mechanize;
use URI;
use HTTP::Request;
use Cpanel::JSON::XS qw(decode_json);
my ( $self, $registry ) = @_;
my $mech = WWW::Mechanize->new();
my $root = URI->new("http://en.wikipedia.org/w/api.php");
my $url = $root->clone();
for my $i ( 21 .. 25 ) {
my $chrom = $i;
if ( $chrom == 23 ) {
$chrom = "M";
}
elsif ( $chrom == 24 ) {
$chrom = "Y";
}
elsif ( $chrom == 25 ) {
$chrom = "X";
}
print "Hi!\n The chromosome is $chrom\n";
my $query = {
action => 'query',
format => 'json',
list => 'categorymembers',
cmtitle => "Category:Genes on human chromosome $chrom",
cmlimit => 'max',
cmcontinue => ''
};
$url->query_form($query);
my @gene_pages = ();
eval {
while ( my $response = $mech->get($url) ) {
my $perl_scalar = decode_json( $response->decoded_content() )
; #J Source of malformed JSON string error
push @gene_pages, @{ $perl_scalar->{query}->{categorymembers} };
my $count = @gene_pages;
# Adapted code to new format for continuing queries
if ( $perl_scalar->{continue} ) {
$query->{cmcontinue} = $perl_scalar->{continue}->{cmcontinue};
$url->query_form($query);
}
else {
last;
}
}
};
if ( $@ =~ /malformed/ ) {
redo;
}
my $gene_count = 0;
eval {
foreach my $gene_page (@gene_pages) {
$gene_count++;
my $url = $root->clone();
my $query = {
action => 'query',
prop => 'revisions',
format => 'json',
rvprop => 'content|tags|timestamp',
pageids => $gene_page->{pageid}
};
$url->query_form($query);
# $log->debugf("Requesting: %s", $url->as_string());
my $response = $mech->get($url);
my $content = $response->decoded_content();
my $perl_scalar = decode_json( $response->decoded_content() )
; #J Source of malformed JSON string error
if ( $gene_count % 10 == 0 ) {
print "$gene_count gene pages complete\n";
}
}
};
print "There were $gene_count genes found for chromosome $chrom\n";
}
这段代码的组成部分要大得多,但我将其排除在外,因为我知道这是问题根源所在的区域。
使用WWW::Mechanize
while
循环部分
my $response = $mech->get($url)
与内存泄漏有关。
如果我删除该组件并且 运行 内存使用的程序保持大致相同然后将其添加回去显示内存再次增加。
Perl 版本:5.24.1
系统:Ubuntu16.04
编辑:@Borodin 感谢您如此详尽的回复!不幸的是,我仍然注意到我的计算机上存在内存泄漏,这让我想知道是否还有更大的问题。
它仍然会逐渐占用内存,目前我的电脑还可以,但是当我 运行 包含一些网络抓取的完整程序时,我不知道我的电脑是否足够。
关于一个可能相关的说明 -- 我的计算机有一个奇怪的问题,它有时无法完全下载文件(尽管下载已完成,但文件仍未 运行 分类)。当我 运行ning 你的程序时,我经常遇到这个错误:
** 解析 JSON 字符串时字符串意外结束,字符偏移量为 5506(在“(字符串结束)”之前)**
这似乎与我遇到的问题有关,我想知道这是否会导致内存泄漏问题?
您不使用 WWW::Mechanize
中 LWP::UserAgent
未提供的任何部分,因此我建议您推迟到后者
这是一些工作代码,其功能与您自己的程序几乎相同。它对我来说没有任何内存泄漏
请询问您是否需要任何解释;整个程序内容太多
#!/usr/bin/env perl
use strict;
use warnings 'all';
use URI;
use URI::QueryParam;
use LWP;
use JSON::XS qw(decode_json);
STDOUT->autoflush;
my $api_root = URI->new( 'http://en.wikipedia.org/w/api.php' );
my @chromosomes = ( 1 .. 22, qw/ M Y X/ );
my $ua = LWP::UserAgent->new;
for my $chrom ( @chromosomes[20..$#chromosomes] ) {
#print "The chromosome is $chrom\n";
my $query = {
action => 'query',
format => 'json',
list => 'categorymembers',
cmtitle => "Category:Genes on human chromosome $chrom",
cmlimit => 'max',
};
my $url = $api_root->clone;
$url->query_form( $query );
my @gene_pages;
while () {
my $resp = $ua->get( $url );
die $resp->status_line unless $resp->is_success;
# J Source of malformed JSON string error
my $data = decode_json( $resp->decoded_content );
my $query = $data->{query};
my $continue = $data->{continue};
push @gene_pages, @{ $query->{categorymembers} };
# Adapted code to new format for continuing queries
last unless $continue;
$url->query_param( cmcontinue => $continue->{cmcontinue} );
}
printf "Processing %d gene pages for chromosome %s\n",
scalar @gene_pages,
$chrom;
my $gene_count;
for my $gene_page ( @gene_pages ) {
++$gene_count;
my $url = $api_root->clone;
my $query = {
action => 'query',
prop => 'revisions',
format => 'json',
rvprop => 'content|tags|timestamp',
pageids => $gene_page->{pageid}
};
$url->query_form( $query );
# print "Requesting: $url\n";
my $resp = $ua->get( $url );
die $resp->status_line unless $resp->is_success;
my $content = $resp->decoded_content;
my $data = decode_json( $content ); # J Source of malformed JSON string error
print "$gene_count gene pages complete\n" unless $gene_count % 10;
}
print "There were $gene_count genes found for chromosome $chrom\n";
}