2009-08-07 3 views
12

У меня возникли проблемы с памятью в Perl. Когда я заполняю большой хэш, я не могу вернуть память обратно в ОС. Когда я делаю то же самое со скаляром и использую undef, он вернет память ОС.В Perl, как я могу освободить память в операционной системе?

Вот тестовая программа, которую я написал.

#!/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.

+0

Вам нужно переформатировать сообщение. Это невозможно. – EightyEight

+0

Я могу заверить вас, что Perl не использует всего 2.7K. ps сообщает о памяти в 1 тыс. кусков, использование памяти в 1024 раза слишком низкое. – Schwern

ответ

12

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

См. Часто задаваемые вопросы: How can I free an array or hash so my program shrinks?.

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

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

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

Например, How can I make my Perl program take less memory?, вероятно, относится к вашей проблеме.

+2

Ваша ссылка RTFF очень хорошая. Он указывает, что это зависит от ОС. Если ваша ОС поддерживает его, вы можете освободить память обратно в ОС. Я написал код, который делает именно то, что OP хочет использовать ActivePerl на WinXP. Нет необходимости в дополнительной враждебности, пожалуйста, подумайте об исправлении вашего первого абзаца. – daotoad

+0

Я обезвредил эту атакующую бомбу. Нам нужны такие люди, как вы, с rep> 10k! Не рискуйте, пожалуйста. – innaM

+1

@daotoad и Manni Это был вопрос времени. Когда я это написал, исходным сообщением был malformatted беспорядок, и единственное, что я мог различить, - это самые первые строки. См. Также комментарий от EightyEight. Во всяком случае, спасибо, что позаботились об этом. –

19

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

Вы можете хранить гигабайты данных в виде хэша таким образом, и y ou будет использовать только небольшой бит памяти. (В принципе, любой пейджер BDB решает сохранить в памяти, это управляемо.)

+1

+1 Отличная демонстрация рекомендаций, приведенных в последней части ответа на часто задаваемые вопросы: http://faq.perl.org/perlfaq3.html#How_can_I_make_my_Pe1 –

+3

Часто задаваемые вопросы о производительности, как правило, вы попадаете в кеш, и это не так более дорогостоящим (с точки зрения времени), чем доступ к структуре в памяти. (И как только вы начинаете замену, структуры в памяти ужасающе медленны, так как хэши не имеют хорошей локальности ссылок. Я помню, как писал некоторые сценарии ETL, которые на несколько порядков выполнялись быстрее с привязанными хэшами BDB вместо собственных хэшей.) – jrockway

+0

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

8

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

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

+4

Этот ответ не заслуживает ниспроверки. Разветвленный процесс - вполне разумный способ справиться с четко определенными временными всплесками в использовании памяти в программах с длительным сроком службы. –

+0

Проблемы: сервер имеет 3 ГБ оперативной памяти. 1 ГБ для ОС и 1 ГБ для MySQL. Мой процесс начнется в 27 мб, и он достигнет около 800 мб. Затем система начнет сворачивать и все замедлит. Проблема с вилкой в ​​том, что она скопирует все 800mb в новый процесс. – clintonm9

+0

Кроме того, чтобы добавить к этому больше; Я использую разные потоки, чтобы делать разные вещи асинхронными. использовать потолки; использовать темы :: shared; use Thread :: Queue; Таким образом, я передам данные в общий хеш, тогда другой поток обработает данные по мере их поступления. Это передается многим потокам, выполняющим разные вещи. Я предполагаю, что в какой-то момент хеш становится очень большим и занимает много памяти. Возможно, есть лучший способ во время этого вообще? Моя проблема с другим процессом вилки - это намного сложнее передавать данные взад и вперед. Любые мысли? – clintonm9

0

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

Смежные вопросы