2010-07-29 17 views
6

He estado trabajando en un proyecto de Perl en el trabajo, y encontré una extraña pérdida de memoria. He reducía la fuente de mi problema en un ejemplo artificioso:Perl map/grep memory leak

#!/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($_); 
#} 

Descomentar 1 de los 4 bloques de código (por debajo de las subrutinas) a la vez y ejecutar el script durante el seguimiento de su uso de memoria. En mi máquina, el código que usa grep o mapa parece causar pérdidas de memoria, mientras que el "lazo equivalente" no lo hace. Mi versión perl es v5.10.1 y estoy ejecutando Ubuntu.

Creo que esto podría ser un error en Perl, pero no quiero llegar a una conclusión drástica sin otra opinión sobre cuál podría ser la causa. ¿Alguien puede explicar si este comportamiento es correcto?

Gracias

Respuesta

2

No sé si se trata de una pérdida de memoria como tal. Si baje el valor superior de su ciclo (por ejemplo, de 100000 a 100), puedo usar las expresiones map/grep repetidamente sin perder memoria.

Por el contrario, parece más probable que map y grep sean operaciones atómicas cuando se trata de la administración de memoria, que perl no está realizando su recolección de basura en el medio de esas operaciones.

Perl 5.12.0 (y 5.8.9) parecen un poco más robustos en este tipo de expresiones (pero también parecen ser más lentas).

1

Realmente lo es. Pero para demostrarlo, debe poner while (1) {} alrededor de la expresión sospechosa: en perl, la memoria que se adquiere nunca vuelve al sistema operativo (pero Perl puede reutilizarla). Me he encontrado con código

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

bajo 5.8.8 y crece constantemente en el tamaño - es así, es una fuga .