我在Perl中遇到了一些内存问题。当我填满一个大哈希时,我无法将内存释放回操作系统。当我使用标量执行相同操作并使用undef
时,它会将内存返回给操作系统。
这是我写的测试程序。
#!/usr/bin/perl
###### Memory test
######
## Use Commands
use Number::Bytes::Human qw(format_bytes);
use Data::Dumper;
use Devel::Size qw(size total_size);
## Create Varable
my $share_var;
my %share_hash;
my $type_hash = 1;
my $type_scalar = 1;
## Start Main Loop
while (true) {
&Memory_Check();
print "Hit Enter (add to memory): "; <>;
&Up_Mem(100_000);
&Memory_Check();
print "Hit Enter (Set Varable to nothing): "; <>;
$share_var = "";
$share_hash = ();
&Memory_Check();
print "Hit Enter (clean data): "; <>;
&Clean_Data();
&Memory_Check();
print "Hit Enter (start over): "; <>;
}
exit;
#### Up Memory
sub Up_Mem {
my $total_loops = shift;
my $n = 1;
print "Adding data to shared varable $total_loops times\n";
until ($n > $total_loops) {
if ($type_hash) {
$share_hash{$n} = 'X' x 1111;
}
if ($type_scalar) {
$share_var .= 'X' x 1111;
}
$n += 1;
}
print "Done Adding Data\n";
}
#### Clean up Data
sub Clean_Data {
print "Clean Up Data\n";
if ($type_hash) {
## Method to fix hash (Trying Everything i can think of!
my $n = 1;
my $total_loops = 100_000;
until ($n > $total_loops) {
undef $share_hash{$n};
$n += 1;
}
%share_hash = ();
$share_hash = ();
undef $share_hash;
undef %share_hash;
}
if ($type_scalar) {
undef $share_var;
}
}
#### Check Memory Usage
sub Memory_Check {
## Get current memory from shell
my @mem = `ps aux | grep \"$$\"`;
my($results) = grep !/grep/, @mem;
## Parse Data from Shell
chomp $results;
$results =~ s/^\w*\s*\d*\s*\d*\.\d*\s*\d*\.\d*\s*//g; $results =~ s/pts.*//g;
my ($vsz,$rss) = split(/\s+/,$results);
## Format Numbers to Human Readable
my $h = Number::Bytes::Human->new();
my $virt = $h->format($vsz);
my $h = Number::Bytes::Human->new();
my $res = $h->format($rss);
print "Current Memory Usage: Virt: $virt RES: $res\n";
if ($type_hash) {
my $total_size = total_size(\%share_hash);
my @arr_c = keys %share_hash;
print "Length of Hash: " . ($#arr_c + 1) . " Hash Mem Total Size: $total_size\n";
}
if ($type_scalar) {
my $total_size = total_size($share_var);
print "Length of Scalar: " . length($share_var) . " Scalar Mem Total Size: $total_size\n";
}
}
输出:
./Memory_Undef_Simple.cgi Current Memory Usage: Virt: 6.9K RES: 2.7K Length of Hash: 0 Hash Mem Total Size: 92 Length of Scalar: 0 Scalar Mem Total Size: 12 Hit Enter (add to memory): Adding data to shared varable 100000 times Done Adding Data Current Memory Usage: Virt: 228K RES: 224K Length of Hash: 100000 Hash Mem Total Size: 116813243 Length of Scalar: 111100000 Scalar Mem Total Size: 111100028 Hit Enter (Set Varable to nothing): Current Memory Usage: Virt: 228K RES: 224K Length of Hash: 100000 Hash Mem Total Size: 116813243 Length of Scalar: 0 Scalar Mem Total Size: 111100028 Hit Enter (clean data): Clean Up Data Current Memory Usage: Virt: 139K RES: 135K Length of Hash: 0 Hash Mem Total Size: 92 Length of Scalar: 0 Scalar Mem Total Size: 24 Hit Enter (start over):
因此,您可以看到内存中断,但它只会降低标量的大小。任何想法如何释放哈希的记忆?
同样Devel::Size
显示哈希只占用92个字节,即使程序仍在使用139K。
答案 0 :(得分:19)
通常,是的,这就是UNIX上的内存管理工作方式。如果您使用的是最近的glibc Linux,并且正在使用该malloc,则可以将自由内存返回给操作系统。不过,我不确定Perl会这样做。
如果您想使用大型数据集,请不要将整个内容加载到内存中,使用类似BerkeleyDB的内容:
https://metacpan.org/pod/BerkeleyDB
示例代码,逐字窃取:
use strict ;
use BerkeleyDB ;
my $filename = "fruit" ;
unlink $filename ;
tie my %h, "BerkeleyDB::Hash",
-Filename => $filename,
-Flags => DB_CREATE
or die "Cannot open file $filename: $! $BerkeleyDB::Error\n" ;
# Add a few key/value pairs to the file
$h{apple} = "red" ;
$h{orange} = "orange" ;
$h{banana} = "yellow" ;
$h{tomato} = "red" ;
# Check for existence of a key
print "Banana Exists\n\n" if $h{banana} ;
# Delete a key/value pair.
delete $h{apple} ;
# print the contents of the file
while (my ($k, $v) = each %h)
{ print "$k -> $v\n" }
untie %h ;
(好的,不是逐字的。他们使用use vars
是......遗产......)
您可以通过这种方式将千兆字节的数据存储在哈希中,并且您只使用一点点内存。 (基本上,无论BDB的寻呼机决定保留在内存中,这都是可控制的。)
答案 1 :(得分:12)
通常,您不能指望perl向操作系统释放内存。
请参阅常见问题解答:How can I free an array or hash so my program shrinks?。
你通常不能。分配给词汇(即
my()
变量)的内存即使超出范围也无法回收或重用。如果变量返回范围,则保留它。分配给全局变量的内存可以使用undef()
和/或delete()
重复使用(在您的程序中)。在大多数操作系统上,分配给程序的内存永远不会返回给系统。这就是为什么长期运行的程序有时会重新执行的原因。某些操作系统(特别是使用
mmap(2)
分配大块内存的系统)可以回收不再使用的内存,但在这样的系统上,必须配置和编译perl才能使用操作系统malloc
,而不是perl
。
在浪费时间之前,最好先阅读计算机上安装的FAQ list。
例如,How can I make my Perl program take less memory?可能与您的问题有关。
答案 2 :(得分:8)
为什么要让Perl向操作系统释放内存?你可以使用更大的交换。
如果你真的必须,在分叉的过程中完成你的工作,然后退出。
答案 3 :(得分:0)
尝试使用选项-Uusemymalloc重新编译perl以使用系统malloc和free。您可能会看到一些不同的结果