在Perl中,如何向操作系统释放内存?

时间:2009-08-07 04:00:58

标签: perl memory-management

我在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。

4 个答案:

答案 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。您可能会看到一些不同的结果