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

cli*_*nm9 12 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";
    }

}
Run Code Online (Sandbox Code Playgroud)

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.

jro*_*way 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 ;
Run Code Online (Sandbox Code Playgroud)

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

您可以通过这种方式将千兆字节的数据存储在哈希中,并且您将只使用一小部分内存.(基本上,无论BDB的寻呼机决定保留在内存中,这都是可控制的.)

  • 常见问题解答在性能方面是错误的,通常你会遇到缓存,这并不比访问内存结构更昂贵(就时间而言).(一旦你开始交换,内存结构的速度非常慢,因为哈希没有很好的引用位置.我记得编写了一些ETL脚本,它们使用绑定的BDB哈希而不是本机哈希,速度提高了几个数量级.) (3认同)

Sin*_*nür 12

通常,您不能指望perl向操作系统释放内存.

请参阅常见问题解答:如何释放数组或散列,以便我的程序缩小?.

你通常不能.分配给词汇(即my()变量)的内存即使超出范围也无法回收或重用.如果变量返回范围,则保留它.分配给全局变量的内存可以通过使用undef()和/或重用(在您的程序中)delete().

在大多数操作系统上,分配给程序的内存永远不会返回给系统.这就是为什么长期运行的程序有时会重新执行的原因.某些操作系统(特别是mmap(2)用于分配大块内存的系统)可以回收不再使用的内存,但在这样的系统上,必须配置和编译perl以使用操作系统malloc,而不是perl.

在浪费您的时间之前,阅读也安装在您计算机上的常见问题列表始终是一个好主意.

例如,如何让我的Perl程序占用更少的内存?可能与您的问题有关.

  • 你的RTFF链接非常好.它指出这是依赖于操作系统的.如果您的操作系统支持它,您可以将内存释放回操作系统.我编写的代码完全符合OP在WinXP上使用ActivePerl的要求.没有必要额外的敌意,请考虑修复你的第一段. (2认同)

yst*_*sth 8

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

如果你真的必须,在分叉的过程中完成你的工作,然后退出.

  • 这个答案不值得投票.分叉进程是处理长时间运行程序中内存使用中明确定义的临时峰值的完美合理方法. (4认同)
  • @clintonm9:同样,将你在此工作的时间成本与额外内存(甚至是额外的服务器)的成本进行比较...... (3认同)
  • @clintonm9:哎哟.您是否知道共享数据实际上在每个线程中都有一个副本?尝试将数据存储在绑定的数据库或SQLite中,看看它是否足够快以满足您的需求.如果不是,你将不得不从头开始重新思考. (2认同)