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

У меня проблемы с памятью в 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 
Текущее использование памяти: Virt: 6,9K  RES: 2,7K
Длина хеша: 0 Hash Mem Общий размер: 92
Длина скаляра: 0 Scalar Mem Общий размер: 12
Нажмите Enter (добавить в память): 
Добавление данных в общий Varable 100000 раз
Готово Добавление данных
Текущее использование памяти: Virt: 228K  RES: 224K
Длина хэша: 100000 хэш-мем. Общий размер: 116813243
Длина скаляра: 111100000 Скалярная память Общий размер: 111100028
Нажмите Enter (установите Varable на ничего): 
Текущее использование памяти: Virt: 228K  RES: 224K
Длина хэша: 100000 хэш-мем. Общий размер: 116813243
Длина скаляра: 0 Scalar Mem Общий размер: 111100028
Нажмите Enter (очистить данные): 
Очистить данные
Текущее использование памяти: Virt: 139K  RES: 135K
Длина хеша: 0 Hash Mem Общий размер: 92
Длина скаляра: 0 Scalar Mem Общий размер: 24
Нажмите Enter (начать заново): 

Итак, как вы можете видеть, память уменьшается, но уменьшается только на размер скаляра. Есть идеи как освободить память от хэша?

Также Devel::Size показывает, что хеш занимает только 92 байта, хотя программа все еще использует 139K.

4 ответа

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам