简体   繁体   English

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

[英]In Perl, how can I release memory to the operating system?

I am having some problems with memory in Perl. 我在Perl中遇到了一些内存问题。 When I fill up a big hash, I can not get the memory to be released back to the OS. 当我填满一个大哈希时,我无法将内存释放回操作系统。 When I do the same with a scalar and use undef , it will give the memory back to the OS. 当我使用标量并使用undef ,它会将内存返回给操作系统。

Here is a test program I wrote. 这是我写的测试程序。

#!/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: 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):

So as you can see the memory goes down, but it only goes down the size of the scalar. 所以你可以看到内存下降,但它只会下降到标量的大小。 Any ideas how to free the memory of the hash? 任何想法如何释放哈希的记忆?

Also Devel::Size shows the hash is only taking up 92 bytes even though the program still is using 139K. 另外Devel::Size显示哈希只占用92个字节,即使程序仍然使用139K。

Generally, yeah, that's how memory management on UNIX works. 通常,是的,这就是UNIX上的内存管理工作方式。 If you are using Linux with a recent glibc, and are using that malloc, you can return free'd memory to the OS. 如果您使用的是最近的glibc Linux,并且正在使用该malloc,则可以将自由内存返回给操作系统。 I am not sure Perl does this, though. 不过,我不确定Perl会这样做。

If you want to work with large datasets, don't load the whole thing into memory, use something like BerkeleyDB: 如果您想使用大型数据集,请不要将整个内容加载到内存中,使用类似BerkeleyDB的内容:

https://metacpan.org/pod/BerkeleyDB https://metacpan.org/pod/BerkeleyDB

Example code, stolen verbatim: 示例代码,被逐字逐句:

  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, not verbatim. Their use of use vars is ... legacy ...) (好的,不是逐字的。他们对use varsuse vars是......遗产...)

You can store gigabytes of data in a hash this way, and you will only use a tiny bit of memory. 您可以通过这种方式将千兆字节的数据存储在哈希中,并且您将只使用一点点内存。 (Basically, whatever BDB's pager decides to keep in memory; this is controllable.) (基本上,无论BDB的寻呼机决定保留在内存中,这都是可控制的。)

In general, you cannot expect perl to release memory to the OS. 通常,您不能指望perl向操作系统释放内存。

See the FAQ: How can I free an array or hash so my program shrinks? 请参阅常见问题解答: 如何释放数组或散列,以便我的程序缩小? .

You usually can't. 你通常不能。 Memory allocated to lexicals (ie my() variables) cannot be reclaimed or reused even if they go out of scope. 分配给词汇(即my()变量)的内存即使超出范围也无法回收或重用。 It is reserved in case the variables come back into scope. 如果变量返回范围,则保留它。 Memory allocated to global variables can be reused (within your program) by using undef() and/or delete() . 分配给全局变量的内存可以通过使用undef()和/或delete()重用(在程序中delete()

On most operating systems, memory allocated to a program can never be returned to the system. 在大多数操作系统上,分配给程序的内存永远不会返回给系统。 That's why long-running programs sometimes re- exec themselves. 这就是为什么长期运行的程序有时会重新执行的原因。 Some operating systems (notably, systems that use mmap(2) for allocating large chunks of memory) can reclaim memory that is no longer used, but on such systems, perl must be configured and compiled to use the OS's malloc , not perl 's. 一些操作系统(特别是使用mmap(2)分配大块内存的系统)可以回收不再使用的内存,但是在这样的系统上,必须配置和编译perl以使用OS的malloc ,而不是perl的。

It is always a good idea to read the FAQ list , also installed on your computer, before wasting your time. 在浪费您的时间之前,阅读也安装在您计算机上的常见问题列表始终是一个好主意。

For example, How can I make my Perl program take less memory? 例如, 如何让我的Perl程序占用更少的内存? is probably relevant to your issue. 可能与您的问题有关。

Why do you want Perl to release the memory to the OS? 为什么要让Perl向操作系统释放内存? You could just use a larger swap. 你可以使用更大的交换。

If you really must, do your work in a forked process, then exit. 如果你真的必须,在分叉的过程中完成你的工作,然后退出。

Try recompiling perl with the option -Uusemymalloc to use the system malloc and free. 尝试使用选项-Uusemymalloc重新编译perl以使用系统malloc和free。 You might see some different results 您可能会看到一些不同的结果

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM