2010-02-08 6 views
13

Estoy buscando un par de posibles pérdidas de memoria en una base de código Perl y me gustaría saber acerca de las trampas comunes con respecto a la administración de memoria (errores) en Perl.Patrones de fuga comunes de memoria/referencia de Perl?

¿Qué patrones de fuga comunes ha observado en el código Perl?

+0

¿Cómo atrapó estas fugas de memoria? ¿Has probado Perl Profiler (http://www.perlmonks.org/?node_id=472366)? –

Respuesta

18

Las referencias circulares son de lejos la más común la causa canónica de fugas.

sub leak { 
    my ($foo, $bar); 
    $foo = \$bar; 
    $bar = \$foo; 
} 

Perl usa la cuenta de referencia para la recolección de basura. Esto significa que perl lleva un recuento de qué punteros a cualquier variable existen en un momento dado. Si la variable sale del alcance y el recuento es 0, la variable se borra.

En el código de ejemplo anterior, $foo y $bar Nunca se recogen y una copia persistirá después de cada invocación de leak() debido a que ambas variables tienen un contador de referencia de 1.

La forma más sencilla para evitar este problema es utilizar referencias débiles Las referencias débiles son referencias que sigue para acceder a los datos, pero no cuentan para la recolección de basura.

use Scalar::Util qw(weaken); 

sub dont_leak { 
    my ($foo, $bar); 
    $foo = \$bar; 
    $bar = \$foo; 
    weaken $bar; 
} 

En dont_leak(), $foo tiene un contador de referencia de 0, $bar tiene un recuento de ref 1. Cuando dejamos el alcance de la subrutina, $foo se devuelve a la piscina, y se borra su referencia a $bar. Esto reduce el conteo de ref en $bar a 0, lo que significa que $bar también puede regresar al grupo.

Actualización: brain d foy me preguntaron si tenía datos para respaldar mi afirmación de que las referencias circulares son comunes. No, no tengo ninguna estadística para mostrar que las referencias circulares son comunes. Son la forma más común y mejor documentada de pérdidas de memoria perl.

Mi experiencia es que suceden. Aquí hay un resumen rápido de las pérdidas de memoria que he visto durante más de una década de trabajo con Perl.

He tenido problemas con las aplicaciones pTk que desarrollan fugas. Algunas filtraciones que pude probar se debieron a referencias circulares que surgieron cuando Tk pasa las referencias de ventana. También he visto fugas de pTk cuya causa nunca podría rastrear.

He visto a la gente malinterpretar weaken y terminar con referencias circulares por accidente.

He visto ciclos involuntarios surgir cuando demasiados objetos mal pensados ​​se juntan a toda prisa.

En una ocasión encontré fugas de memoria que provenían de un módulo XS que estaba creando estructuras de datos grandes y profundas. Nunca pude obtener un caso de prueba reproducible que fuera más pequeño que todo el programa. Pero cuando reemplacé el módulo con otro serializador, las filtraciones desaparecieron. Entonces sé que esas filtraciones vinieron del XS.

Por lo tanto, en mi experiencia los ciclos son una fuente importante de fugas.

Afortunadamente, there is a module para ayudar a rastrearlos.

En cuanto a si las grandes estructuras globales que nunca se limpian constituyen "fugas", estoy de acuerdo con Brian. Graznan como goteras (tenemos un uso cada vez mayor de la memoria de proceso debido a un error), por lo que son fugas. Aun así, no recuerdo haber visto este problema en particular en la naturaleza.

Basado en lo que veo en el sitio de Stonehenge, supongo que Brian ve un montón de código de enfermedad por parte de las personas para las que está entrenando o que está preparando milagros curativos. Entonces, su conjunto de muestras es mucho más grande y variado que el mío, pero tiene su propio sesgo de selección.

¿Qué causa de fugas es la más común? No creo que lo sabremos nunca. Pero todos podemos estar de acuerdo en que las referencias circulares y los depósitos de datos globales son antipatrones que deben eliminarse siempre que sea posible, y deben manejarse con cuidado y precaución en los pocos casos en que tengan sentido.

+2

¿Hay algo para apoyar referencias circulares como la forma más común? Casi nunca veo el problema, pero sí veo personas con hashes globales que nunca eliminan. –

+4

@brian d foy: Estrictamente hablando, eso no es una pérdida de memoria, solo un uso excesivo de memoria. No es una * pérdida * de memoria * hasta que el programa * no puede * liberar la memoria. – cjm

+1

A la mayoría de las personas no les importa estrictamente por qué su memoria crece constantemente lentamente (o no tan lentamente). :) –

5

He tenido problemas con XS en el pasado, tanto mis cosas enrolladas a mano como los módulos de CPAN, donde la memoria se filtra desde el código C si no se gestiona correctamente. Nunca logré rastrear las filtraciones; el proyecto tenía una fecha límite ajustada y tenía una vida operativa fija, así que guardé el problema con un reinicio diario de cron. cron es realmente maravilloso.

9

Si el problema está en el código de Perl, es posible que tenga una referencia que señale a sí misma o un nodo padre.

Suele venir en forma de un objeto, que hace referencia a un objeto principal.

{ package parent; 
    sub new{ bless { 'name' => $_[1] }, $_[0] } 
    sub add_child{ 
    my($self,$child_name) = @_; 
    my $child = child->new($child_name,$self); 
    $self->{$child_name} = $child; # saves a reference to the child 
    return $child; 
    } 
} 
{ package child; 
    sub new{ 
    my($class,$name,$parent) = @_; 
    my $self = bless { 
     'name' => $name, 
     'parent' => $parent # saves a reference to the parent 
    }, $class; 
    return $self; 
    } 
} 
{ 
    my $parent = parent->new('Dad'); 
    my $child = parent->add_child('Son'); 

    # At this point both of these are true 
    # $parent->{Son}{parent} == $parent 
    # $child->{parent}{Son} == $child 

    # Both of the objects **would** be destroyed upon leaving 
    # the current scope, except that the object is self-referential 
} 

# Both objects still exist here, but there is no way to access either of them. 

La mejor manera de solucionar este problema es utilizar Scalar::Util::weaken.

use Scalar::Util qw'weaken'; 
{ package child; 
    sub new{ 
    my($class,$name,$parent) = @_; 
    my $self = bless { 
     'name' => $name, 
     'parent' => $parent 
    }, $class; 

    weaken ${$self->{parent}}; 

    return $self; 
    } 
} 

Yo recomendaría eliminar la referencia al objeto principal, desde el niño, si es posible.

+2

Vale la pena mencionar que con los objetos, también puede usar un destructor para romper cualquier referencia circular. Su clase Child podría tener 'sub DESTROY {$ _ [0] -> {parent} = undef; } ', o incluso' sub DESTROY {$ _ [0] = undef; } 'y no hay necesidad de referencias débiles. Los árbitros débiles son una mejor manera de manejar las cosas, hacen que el comportamiento correcto sea automático. También vale la pena señalar que Moose presenta un debilitamiento de referencia automático para los atributos: http://search.cpan.org/dist/Moose/lib/Moose/Manual/Attributes.pod#Weak_references Es otra forma en la que Moose mejora el OO Perl. – daotoad

+4

@daotoad: No se puede usar un destructor para romper referencias circulares, porque el destructor no se llama hasta que el recuento de referencias caiga a cero. Si se llama al destructor, no tiene un problema de referencia circular. Si tiene referencias circulares, debe usar refs débiles o llamar manualmente a un método destructor cuando haya terminado con un objeto (que es propenso a errores). – cjm

+1

@cjm, tienes razón. Ha sido un tiempo. 'debilitar' es realmente el camino a seguir. Debe crear un objeto contenedor para contener la referencia circular y dejar que el destructor del contenedor rompa el ciclo. La otra opción es tener una llamada de método explícita que borre un widget y rompa cualquier ciclo. Por ejemplo, el método 'Widget :: destroy()' de Perl Tk. – daotoad

2

Algunos módulos de CPAN utilizan referencias circulares para hacer su trabajo, p. HTML::TreeBuilder (que representa el árbol HTML). Le exigirán que ejecute algún método/rutina de destrucción al final. Solo lea los documentos :)

Cuestiones relacionadas