2010-09-27 102 views
20

如何彻底删除Perl中的包?这不仅意味着包变量,还包括Perl更新来处理继承更改和其他事情的任何魔术表。如何完全删除Perl中的包?

这个简单的测试:

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

leaktrace { 
    package test; 
    our $x = 1; 

    package main; 
    delete_package 'test'; 
}; 

导致以下输出:

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. 

使用-verbose标志leaktrace结果,其中我可以张贴在请求数据的screenfuls。

事情变得更糟,如果线our @ISA = 'main';添加到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. 

第32行是在our @ISA是。

要说明的是,这些确实泄漏,而不仅仅是从解释噪音:

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

会以恒定的速率

所以吃内存,有没有更好的办法来摆脱包比Symbol的delete_package?我还需要做些什么来帮助它?

我已经看到了相同的行为在5.8.8,5.9.1和5.12

+0

一个很好的问题,我的好奇心被激怒了,但我必须问:为什么? – 2010-09-27 20:20:13

+5

在我的CPAN模块'List :: Gen'上,(http://search.cpan.org/perldoc?List::Gen),我有一个实用函数'curse',它将一个基于闭包的对象安装到一个临时包中(以方便标准方法调用(高速))。 'delete_package'清除所有内容,但由于上述问题,'curse'仍然会泄漏内存。泄漏并不是很大,但它在那里,如果可能的话,我想插上它。 – 2010-09-27 20:27:53

+3

如果您还没有,请将其作为perl错误进行归档。 – ysth 2010-09-28 15:01:15

回答

4

所以这是Perl中的一个错误,即使你发现了一个报告的一个。解决这个问题的办法很简单,似乎你唯一能避免这些泄漏的方法就是选择另一种方法来解决你的问题。

你为什么需要一个半匿名包而不是例如一个闭包?这些非常容易,不会泄漏,并且通过一些创造性,你仍然可以在它们之上实现几乎所有的外部接口,例如通过祝福你的闭包代码并为它们提供方法,为它们提供超载等。

+0

由于使用这些对象的方式(严密的内部循环计算),我试图避免多重重定向。因此,'curse'将其对象安装到一个包中,以便您可以调用'$ obj-> method'(并保留一些继承的外观),而不是暴露实现:'$$ obj {method}()'。我可以使用'AUTOLOAD'或将存根方法安装到父类中,但在每种情况下,它至少需要另外一次子例程调用,并且每次调用至少有一次哈希查找。 – 2010-09-30 18:12:18

+0

我不认为半匿名包是优化这类事情的好方法。首先,方法调用,即使是在第一次调用之后perl缓存方法解析,也比纯调用coderef要慢一些。另外,将参数传递给函数的速度明显慢于构建关闭它所需参数的coderef。当我说“对象”时,我也不是指你使用'$$ obj {method}()',而是祝福coderef本身。此外,使用'AUTOLOAD'似乎是优化内部循环的一种不好的方式 - 它很慢**。 – rafl 2010-10-01 14:10:03

+0

由于我的第一条评论中提到的原因,AUTOLOAD和存根方法已被排除。不过,我仍然需要为最终用户提供某种接口,因为可以在对象上调用很多方法。我发现的最快(运行时,不是创建时间)方法是将基于闭包的方法安装到匿名包中,然后提供一个引用到该包中的引用。看一下'List :: Gen :: curse'和它的实现生成器的例子。如果你有更好的方法来优化它,让我知道 – 2010-10-03 01:03:26