В Perl, как я могу освободить память для операционной системы? - PullRequest
12 голосов
/ 07 августа 2009

У меня проблемы с памятью в 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 ]

19 голосов
/ 07 августа 2009

Как правило, да, именно так работает управление памятью в UNIX. Если вы используете Linux с недавним glibc и используете этот 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 решает сохранить в памяти; это управляемо.)

12 голосов
/ 07 августа 2009

В общем случае нельзя ожидать, что perl освободит память для ОС.

См. FAQ: Как я могу освободить массив или хеш, чтобы моя программа сократилась? .

Вы обычно не можете. Память, выделенная для лексических единиц (т. Е. my() переменных), не может быть восстановлена ​​или использована повторно, даже если они выходят за рамки. Он зарезервирован на случай, если переменные вернутся в область видимости. Память, выделенная для глобальных переменных, может быть повторно использована (в вашей программе) с помощью undef() и / или delete().

В большинстве операционных систем память, выделенная для программы, никогда не может быть возвращена системе. Вот почему долго работающие программы иногда сами себя исполняют. Некоторые операционные системы (особенно системы, которые используют mmap(2) для выделения больших порций памяти) могут восстанавливать память, которая больше не используется, но в таких системах perl должен быть сконфигурирован и скомпилирован для использования ОС malloc, а не perl s.

Всегда полезно прочитать список часто задаваемых вопросов , также установленный на вашем компьютере, прежде чем тратить время.

Например, Как я могу заставить мою Perl-программу занимать меньше памяти? , вероятно, относится к вашей проблеме.

8 голосов
/ 07 августа 2009

Почему вы хотите, чтобы Perl освободил память для ОС? Вы можете просто использовать больший своп.

Если вам действительно нужно, выполните свою работу в раздвоенном процессе, затем выйдите.

0 голосов
/ 13 августа 2010

Попробуйте перекомпилировать perl с параметром -Uusemymalloc, чтобы использовать систему malloc и free. Вы можете увидеть некоторые другие результаты

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...