【问题标题】:How can I completely delete a package in Perl?如何在 Perl 中完全删除一个包?
【发布时间】:2011-04-17 22:46:59
【问题描述】:

如何在 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.

leaktrace 使用-verbose 标志会产生大量数据,我可以根据要求发布这些数据。

如果将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.10.1 和 5.12 中看到了相同的行为

【问题讨论】:

  • 一个很好的问题,我的好奇心被激起了,但我不得不问:为什么?
  • 在我的 CPAN 模块 List::Gen (search.cpan.org/perldoc?List::Gen) 中,我有一个实用函数 curse,它将基于闭包的对象安装到临时包中(以方便标准方法调用(在高速度))。 delete_package 清理所有内容,但 curse 仍然由于上述问题而泄漏内存。泄漏不是很大,但它就在那里,如果可能的话,我想堵住它。
  • 如果您还没有,请将其归档为 perl 错误。
  • @ysth => 我刚刚浏览了 RT,发现了几个月前的这个错误报告:rt.perl.org/rt3/Public/Bug/Display.html?id=75176你认为这涵盖了它,还是我应该添加另一个?

标签: perl memory-leaks package


【解决方案1】:

所以这是 perl 中的一个错误,即使您发现了,也是一个报告的错误。如果没有解决这个问题,避免这些泄漏的唯一方法似乎是选择另一种方法来解决您的问题。

你为什么需要一个半匿名的包而不是一个闭包?这些很容易做到不泄漏,并且,通过一些创造力,您仍然可以在它们之上实现几乎每个外部接口,例如通过祝福您的闭包代码引用并为它们提供方法,为它们提供重载等。

【讨论】:

  • 由于这些对象的使用方式(紧密的内循环计算),我试图避免多级重定向。所以curse 将其对象安装到一个包中,以便您可以调用$obj->method(并保留一些继承的外观)而不是公开实现:$$obj{method}()。我可以使用AUTOLOAD 或将存根方法安装到父类中,但在每种情况下,它至少会额外调用 1 个子例程,并且每次调用至少要进行一次哈希查找。
  • 我不认为半匿名包是优化这类事情的好方法。一方面,方法调用,即使 perl 在第一次调用后缓存方法解析,也比对 coderef 的普通调用慢一点。此外,将参数传递给函数比构建关闭所需参数的 coderef 慢得多。当我说“对象”时,我也不是说你使用$$obj{method}(),而是祝福coderef本身。此外,使用AUTOLOAD 似乎是优化紧密内部循环的一种不好的方法——它
  • AUTOLOAD 和存根方法由于我第一条评论中提到的原因已经被排除在外。但是,我仍然需要为最终用户提供某种接口,因为可以在对象上调用许多方法。我发现的最快(运行时,而不是创建时)方法是将基于闭包的方法安装到匿名包中,然后在该包中提供一个引用。以List::Gen::curse 及其生成器的实现为例。如果您有更好的优化方法,请告诉我
  • @EvanCarroll 我打算检查一下,但这似乎是一个困难的错误,我认为我需要一些时间才能得到一些结果..
猜你喜欢
  • 2020-08-03
  • 2021-12-14
  • 1970-01-01
  • 1970-01-01
  • 2013-10-13
  • 1970-01-01
  • 2019-07-12
  • 1970-01-01
  • 2016-03-08
相关资源
最近更新 更多