我有一个Perl库,它在运行期间使用了一些(大约3或4个)类的许多对象。
在测试代码时,我想确保它不是太多(我不是在谈论内存泄漏,我知道如何检查)。为此,我想我可以计算每个使用的对象并检查运行期间测试数据的最大值。然后,我会比较获得的数字,并猜测库应该使用多少个对象。
但是,我在实现这个问题上遇到了问题。我想到了两种可能的方式:
拦截Package::new
和Package::DESTROY
。但是,这有一点点,在该包中,new
并不总是返回一个新对象。有时候,它使用一个预先存在的对象(这些对象被用作不可变量,所以它应该不重要)。因此,我必须跟踪每个单独的对象,看它是否存在。
拦截Package::bless
和Package::DESTROY
。这应该有效,但似乎有点不正统。
问题是,哪些方式更有可能成功(也许在类似情况下常用),其次,我如何实现第二种方式(我是否必须覆盖Package::bless
所有有问题的包或只有基类等。)。
答案 0 :(得分:2)
存储已查看对象ID的哈希,以确保只计算每个对象一次。您可以使用Hash::Util::FieldHash或Object::ID。
执行此操作idhash的优势在于它不会人为地保持对象的活着。当每个对象被销毁时,它的条目将从idhash中删除。它还具有跨线程工作的优点。
package Foo;
use strict;
use warnings;
use v5.10;
use Hash::Util::FieldHash qw(idhash register id);
idhash my %objects;
sub new {
my $self = bless {}, shift;
register $self, \%objects;
$objects{$self} = 1;
say "Creating ".id $self;
my $num_objects = keys %objects;
say "There are now $num_objects alive.";
return $self;
}
sub DESTROY {
my $self = shift;
my $num_objects = keys(%objects) - 1;
say "Destroying ".id $self;
say "There are $num_objects left alive.";
}
{
my $obj1 = Foo->new; # 1 object
my $obj2 = Foo->new; # 2 objects
{
my $obj3 = Foo->new; # 3 objects
} # 2 objects
my $obj4 = Foo->new; # 3 objects
} # 0 objects
__END__
Creating 4303384168
There are now 1 alive.
Creating 4303542768
There are now 2 alive.
Creating 4303545192
There are now 3 alive.
Destroying 4303545192
There are 2 left alive.
Creating 4303638136
There are now 3 alive.
Destroying 4303542768
There are 2 left alive.
Destroying 4303384168
There are 1 left alive.
Destroying 4303638136
There are 0 left alive.
或者,由于创建的每个对象都将被销毁,因此只有在对象被销毁时才计算。
答案 1 :(得分:2)
关于如何拦截祝福(不是Package :: bless,祝福是内置的,而不是某种方法),大多数内置都是可覆盖的(参见http://perldoc.perl.org/perlsub.html#Overriding-Built-in-Functions)。替换的bless函数会执行你的跟踪(如果一个对象加入你的目标类),然后调用CORE :: bless来实际执行祝福。
答案 2 :(得分:2)
尝试这样的事情(未经测试):
my $Package_objects = {};
my $override_new = *Package::new{CODE};
*Package::new = sub {
my $self = $override_new->(@_);
# Interpolate $self as string to get "HASH(0x12345)"; save package name
$Package_objects->{ "$self" } = 'Package';
return $self;
};
my $override_dest = *Package::DESTROY{CODE};
*Package::DESTROY = sub {
delete $Package_objects->{ "$_[0]" };
$override_dest->(@_);
};
这可能是最野蛮的方法,但必须在没有第三方模块的情况下工作;)
答案 3 :(得分:1)