2010-09-27 20 views
20

¿Cómo se elimina por completo un paquete en Perl? Esto significa no solo las variables del paquete, sino también cualquier tabla mágica que Perl actualice para manejar los cambios de herencia y otras cosas.¿Cómo puedo eliminar por completo un paquete en Perl?

Esta prueba sencilla:

use warnings; use strict; 
use Test::LeakTrace; 
use Symbol 'delete_package'; 

leaktrace { 
    package test; 
    our $x = 1; 

    package main; 
    delete_package 'test'; 
}; 

resultados en la siguiente salida:

leaked ARRAY(0x81c930) from /lib/perl5/5.10.1/Symbol.pm line 166. 
leaked HASH(0x827760) from /lib/perl5/5.10.1/Symbol.pm line 166. 
leaked SCALAR(0x821920) from /lib/perl5/5.10.1/Symbol.pm line 166. 

Usando la bandera -verbose para leaktrace resultados en pantallas completas de datos que puedo publicar a petición.

cosas empeoran si la línea our @ISA = 'main'; se añade al paquete test:

leaked ARRAY(0x81cd10) from so.pl line 32. 
leaked SCALAR(0x81c930) from so.pl line 32. 
leaked ARRAY(0x8219d0) from so.pl line 32. 
leaked HASH(0x8219c0) from so.pl line 32. 
leaked SCALAR(0x8219b0) from so.pl line 32. 
leaked HASH(0x8219a0) from so.pl line 32. 
leaked SCALAR(0x821970) from /lib/perl5/5.10.1/Symbol.pm line 161. 
leaked HASH(0x821950) from so.pl line 32. 
leaked SCALAR(0x821940) from so.pl line 32. 

línea 32 es donde el our @ISA es.

para ilustrar que estos son de hecho las fugas y no sólo el ruido de la intérprete:

my $num = 0; 
while (1) { 
    no strict 'refs'; 
    @{$num.'::ISA'} = 'main'; 
    delete_package $num++; 
} 

va a comer la memoria a una velocidad constante

Por lo tanto, hay una mejor manera de deshacerse de un paquete que el delete_package de Symbol? ¿Hay algo más que deba hacer para ayudarlo?

he visto el mismo comportamiento en 5.8.8, 5.10.1 y 5.12

+0

Una gran pregunta, mi curiosidad se ha despertado, pero tengo que preguntar: ¿Por qué? –

+5

En mi módulo 'List :: Gen' en CPAN, (http://search.cpan.org/perldoc?List::Gen), tengo una función de utilidad' maldición' que instala un objeto basado en cierre en un paquete temporal (para facilitar las llamadas al método estándar (a alta velocidad)). 'delete_package' limpia todo, pero' curse' todavía pierde memoria debido a los problemas anteriores. La fuga no es enorme, pero está allí, y me gustaría conectarla si es posible. –

+3

Si aún no lo ha hecho, presente esto como un error de Perl. – ysth

Respuesta

4

Así que este es un error en Perl, una reportado uno, así como que has descubierto. A menos que lo solucione, parece que la única manera de evitar estas filtraciones es elegir otro enfoque para resolver su problema.

¿Por qué necesita un paquete semi-anónimo en lugar de, por ejemplo, un cierre? Esos son lo suficientemente fáciles de hacer para que no se filtren y, con un poco de creatividad, puede implementar casi todas las interfaces externas encima de ellos, por ejemplo, bendiciendo sus códigos de cierre y proporcionándoles métodos, proporcionando sobrecarga para ellos, etc.

+0

Debido a la forma en que se usan estos objetos (cálculos ajustados del lazo interno), trato de evitar múltiples niveles de redirección. Así que 'curse' instala sus objetos en un paquete para que pueda llamar a' $ obj-> method' (y retener cierta apariencia de herencia) en lugar de exponer la implementación: '$$ obj {method}()'. Podría usar 'AUTOLOAD' o la instalación de métodos stub en una clase principal, pero en cada caso lo haría en al menos 1 llamada de subrutina adicional, y al menos una búsqueda hash por llamada. –

+0

No creo que los paquetes semianónimos sean una buena forma de optimizar este tipo de cosas. Por un lado, las llamadas a métodos, incluso cuando caché en memoria caché la resolución del método después de la primera llamada, son un poco más lentas que las llamadas a un valor cifrado.Además, pasar argumentos a una función es considerablemente más lento que construir un refrendo que cierra los argumentos que necesita. Y cuando dije "objeto", tampoco quise decir que usas '$$ obj {method}()', sino que bendije el coderef en sí mismo. Además, utilizar 'AUTOLOAD' en absoluto parece una mala forma de optimizar los bucles internos ajustados, es ** lento **. – rafl

+0

'AUTOLOAD' y los métodos stub ya están descartados debido a las razones mencionadas en mi primer comentario. Sin embargo, todavía tengo que proporcionar algún tipo de interfaz para el usuario final, ya que hay muchos métodos que pueden invocarse en un objeto. El método más rápido (en tiempo de ejecución, no de creación) que he encontrado es instalar los métodos basados ​​en cierre en paquetes anon y luego proporcionar una referencia bendecida en ese paquete. eche un vistazo a 'List :: Gen :: curse' y la implementación de sus generadores para ver un ejemplo. si tiene una mejor manera de optimizarlo, hágamelo saber –

Cuestiones relacionadas