2010-07-29 14 views
6

J'ai travaillé sur un projet Perl au travail, et j'ai rencontré une étrange fuite de mémoire. Je l'ai fait bouillir sur la source de mon problème dans un exemple artificiel:Perl map/Grep fuite de mémoire

#!/usr/bin/perl 
use strict; 
use warnings; 

# takes: an array reference 
# returns: 1 
sub g { 
    my ($a) = @_; 
    return 1; 
} 

# takes: nothing 
# returns: the result of applying g on an array reference 
sub f { 
    my @a = ('a') x 131072; # allocate roughly a megabyte 
    return g(\@a); 
} 

# causes a leak: 
#map { f($_) } (1..100000); 

# loop equivalent to map, no leak: 
#my @b; 
#for my $i (1..100000) { 
# push @b, f($_); 
#} 

# causes a leak: 
#grep { f($_) } (1..100000); 

# loop equivalent to grep, no leak: 
#my @b; 
#for my $i (1..100000) { 
# push @b, $i if f($_); 
#} 

Uncomment 1 des 4 blocs de code (sous les sous-routines) à la fois et exécutez le script tout en surveillant son utilisation de la mémoire. Sur ma machine, le code qui utilise grep ou map semble provoquer des fuites de mémoire, alors que les "boucles équivalent" ne le font pas. Ma version perl est v5.10.1, et j'utilise Ubuntu.

Je crois que cela pourrait être un bug dans Perl, mais je ne veux pas sauter à une conclusion radicale sans une autre opinion sur ce qui pourrait être la cause. Quelqu'un peut-il expliquer si ce comportement est correct?

Merci

Répondre

2

Je ne sais pas si elle est une fuite de mémoire en tant que telle. Si je baisse la valeur supérieure de votre boucle (disons de 100000 à 100), je peux utiliser les expressions map/grep à plusieurs reprises sans perdre de mémoire. Au contraire, il semble plus probable que map et grep sont des opérations atomiques en matière de gestion de la mémoire, que perl n'effectue pas sa récupération de place au milieu de ces opérations.

Perl 5.12.0 (et 5.8.9) semblent un peu plus robustes sur ce type d'expressions (mais elles semblent aussi être plus lentes).

1

C'est vraiment le cas. Mais pour le prouver, vous devez mettre while (1) {} autour de l'expression suspecte - dans perl, la mémoire qui est une fois acquise ne retourne jamais à OS (mais peut être réutilisée par perl elle-même). J'ai couru avec le code

while (1) {{grep f ($ _)} (1..100000)}

sous 5.8.8 et augmente constamment en taille - donc, il est une fuite .