2009-08-07 66 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"; 
    } 

} 

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。

+0

您需要重新格式化您的帖子。這是不可讀的。 – EightyEight 2009-08-07 04:03:18

+0

我可以向你保證Perl不會使用2.7K。 ps以1K塊報告內存,你的內存使用率是1024次太低。 – Schwern 2009-08-07 20:31:37

回答

12

一般來說,你不能指望perl向OS釋放內存。

查看FAQ:How can I free an array or hash so my program shrinks?

你通常不能。分配給詞彙的內存(即my()變量)即使超出範圍也不能回收或重用。它保留以防變量回到範圍內。分配給全局變量的內存可以通過使用undef()和/或delete()重新使用(在您的程序中)。

在大多數操作系統上,分配給程序的內存永遠無法返回到系統。這就是爲什麼長時間運行的程序有時會重新執行自己。某些操作系統(特別是使用mmap(2)分配大塊內存的系統)可以回收不再使用的內存,但在此類系統上,必須配置和編譯perl以使用操作系統的malloc而不是perl's。

在浪費時間之前,閱讀也安裝在計算機上的FAQ list總是一個好主意。例如,How can I make my Perl program take less memory?可能與您的問題有關。

+2

你的RTFF鏈接非常好。它指出這是依賴於操作系統的。如果您的操作系統支持它,您可以將內存釋放回操作系統。我編寫的代碼完全符合OP在WinXP上使用ActivePerl的要求。不需要額外的敵意,請考慮修正你的第一段。 – daotoad 2009-08-07 05:54:05

+0

我把這個冒犯性炸彈打了一下。我們需要像你這樣的人有一個代表> 10K!請不要冒這樣的風險。 – innaM 2009-08-07 10:20:40

+1

@daotoad和Manni這是一個計時問題。當我寫這篇文章的時候,原文是一個混亂的格式,我唯一能看到的就是第一行。上面也看到了EightyEight的評論。無論如何,謝謝你照顧它。 – 2009-08-07 11:15:07

19

一般來說,是的,這就是UNIX上內存管理的工作原理。如果您使用的是最近使用glibc的Linux,並且正在使用該malloc,則可以將free'd內存返回給操作系統。不過,我不確定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 ; 

(OK,不是逐字的,他們使用use vars是......遺留......)

你可以以這種方式在一個散列中存儲千兆字節的數據,而y ou只會使用一點點內存。 (基本上,無論BDB的尋呼機決定保留在內存中,這是可控的。)

+1

+1在常見問題解答的最後部分給出的建議非常好的示範:http://faq.perl.org/perlfaq3.html#How_can_I_make_my_Pe1 – 2009-08-07 04:22:37

+3

常見問題解答對於性能是錯誤的,通常是您碰到了緩存,而這不是比訪問內存結構更昂貴(就時間而言)。 (而且一旦你開始交換,內存中的結構會非常緩慢,因爲哈希值沒有很好的參考位置,我記得寫一些ETL腳本的速度比綁定BDB哈希值快幾個數量級,而不是本地哈希。) – jrockway 2009-08-07 04:32:24

+0

@ jrockway我認爲只有在不擔心內存使用情況時性能損失纔會成爲問題:小型數據結構完全適合輕載機器上的內存。 – 2009-08-07 11:29:54

8

爲什麼要讓Perl將內存釋放到操作系統?你可以使用更大的交換。

如果您確實需要,請在分叉過程中完成您的工作,然後退出。

+4

這個答案不值得讚賞。分叉進程是處理長時間運行的程序中內存使用中明確定義的臨時尖峯的完全合理的方式。 – 2009-08-07 11:18:11

+0

問題是,服務器有3 GB的內存。 1GB的操作系統和1GB的MySQL。我的過程將從27mb開始,它將達到約800mb。然後系統將開始進入交換並放慢一切。 fork的問題在於它會將所有800mb複製到新進程。 – clintonm9 2009-08-07 13:13:27

+0

另外,爲了增加更多,我使用不同的線程來異步執行不同的事情。 使用線程; 使用線程::共享; 使用Thread :: Queue; 因此,我將數據傳遞給共享散列,然後另一個線程將處理數據。這會傳遞給許多執行不同任務的線程。我猜在某些時候哈希變得非常大,並佔用大量內存。 也許在這個過程中有更好的方法呢?我在不同的fork過程中遇到的問題是來回傳遞數據似乎更困難。 有什麼想法? – clintonm9 2009-08-07 13:20:47

0

嘗試使用選項-Uusemymalloc重新編譯perl以使用系統malloc並釋放。您可能會看到一些不同的結果