2009-08-07 39 views
12

我在Perl中遇到了一些内存问题。当我填满一个大的散列时,我无法将内存释放回操作系统。当我对标量进行相同操作并使用undef时,它会将内存返回给操作系统。在Perl中,如何释放内存到操作系统?

这是我写的测试程序。

#!/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"; 
    } 

} 

OUTPUT:

 
./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

您需要重新格式化您的帖子。这是不可读的。 – EightyEight 2009-08-07 04:03:18

+0

我可以向你保证Perl不会使用2.7K。 ps以1K块报告内存,你的内存使用率是1024次太低。 – Schwern 2009-08-07 20:31:37

回答

12

一般来说,你不能指望perl向OS释放内存。

查看FAQ:How can I free an array or hash so my program shrinks?

你通常不能。分配给词汇的内存(即my()变量)即使超出范围也不能回收或重用。它保留以防变量回到范围内。分配给全局变量的内存可以通过使用undef()和/或delete()重新使用(在您的程序中)。

在大多数操作系统上,分配给程序的内存永远无法返回到系统。这就是为什么长时间运行的程序有时会重新执行自己。某些操作系统(特别是使用mmap(2)分配大块内存的系统)可以回收不再使用的内存,但在此类系统上,必须配置和编译perl以使用操作系统的malloc而不是perl's。

在浪费时间之前,阅读也安装在计算机上的FAQ list总是一个好主意。例如,How can I make my Perl program take less memory?可能与您的问题有关。

+2

你的RTFF链接非常好。它指出这是依赖于操作系统的。如果您的操作系统支持它,您可以将内存释放回操作系统。我编写的代码完全符合OP在WinXP上使用ActivePerl的要求。不需要额外的敌意,请考虑修正你的第一段。 – daotoad 2009-08-07 05:54:05

+0

我把这个冒犯性炸弹打了一下。我们需要像你这样的人有一个代表> 10K!请不要冒这样的风险。 – innaM 2009-08-07 10:20:40

+1

@daotoad和Manni这是一个计时问题。当我写这篇文章的时候,原文是一个混乱的格式,我唯一能看到的就是第一行。上面也看到了EightyEight的评论。无论如何,谢谢你照顾它。 – 2009-08-07 11:15:07

19

一般来说,是的,这就是UNIX上内存管理的工作原理。如果您使用的是最近使用glibc的Linux,并且正在使用该malloc,则可以将free'd内存返回给操作系统。不过,我不确定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 ; 

(OK,不是逐字的,他们使用use vars是......遗留......)

你可以以这种方式在一个散列中存储千兆字节的数据,而y ou只会使用一点点内存。 (基本上,无论BDB的寻呼机决定保留在内存中,这是可控的。)

+1

+1在常见问题解答的最后部分给出的建议非常好的示范:http://faq.perl.org/perlfaq3.html#How_can_I_make_my_Pe1 – 2009-08-07 04:22:37

+3

常见问题解答对于性能是错误的,通常是您碰到了缓存,而这不是比访问内存结构更昂贵(就时间而言)。 (而且一旦你开始交换,内存中的结构会非常缓慢,因为哈希值没有很好的参考位置,我记得写一些ETL脚本的速度比绑定BDB哈希值快几个数量级,而不是本地哈希。) – jrockway 2009-08-07 04:32:24

+0

@ jrockway我认为只有在不担心内存使用情况时性能损失才会成为问题:小型数据结构完全适合轻载机器上的内存。 – 2009-08-07 11:29:54

8

为什么要让Perl将内存释放到操作系统?你可以使用更大的交换。

如果您确实需要,请在分叉过程中完成您的工作,然后退出。

+4

这个答案不值得赞赏。分叉进程是处理长时间运行的程序中内存使用中明确定义的临时尖峰的完全合理的方式。 – 2009-08-07 11:18:11

+0

问题是,服务器有3 GB的内存。 1GB的操作系统和1GB的MySQL。我的过程将从27mb开始,它将达到约800mb。然后系统将开始进入交换并放慢一切。 fork的问题在于它会将所有800mb复制到新进程。 – clintonm9 2009-08-07 13:13:27

+0

另外,为了增加更多,我使用不同的线程来异步执行不同的事情。 使用线程; 使用线程::共享; 使用Thread :: Queue; 因此,我将数据传递给共享散列,然后另一个线程将处理数据。这会传递给许多执行不同任务的线程。我猜在某些时候哈希变得非常大,并占用大量内存。 也许在这个过程中有更好的方法呢?我在不同的fork过程中遇到的问题是来回传递数据似乎更困难。 有什么想法? – clintonm9 2009-08-07 13:20:47

0

尝试使用选项-Uusemymalloc重新编译perl以使用系统malloc并释放。您可能会看到一些不同的结果