我的问题是关于Perl如何在内部管理对象的数据。
在Perl中创建对象时,新子例程通常会返回对受祝福对象的引用。
以下面的代码为例:
# Create a new object
my $object = Object->new(%data1);
# Create a new object with the same variable
$object = Object->new(%data2);
从第一次致电new
开始,我们创建了$object
,引用了一些有福的%data1
视觉表现:
“\”表示引用。
$object ════> \{bless %data1}
在内存中看起来如下:
“&”象征着一个地址
MEMORY:
----------------------------------
&{bless %data1} ════> bless %data1
然后第二次调用new
时,$object
的值会更改为引用其他一些有福的%data2
视觉表现:
$object ══/ /══> \{bless %data1} # The connection to %data1 is broken
║
╚═══════════> \{bless %data2}
现在内存看起来像这样:
MEMORY:
----------------------------------
&{bless %data1} ════> bless %data1
&{bless %data2} ════> bless %data2
现在问题是$object
不再存储引用\{bless %data1}
,地址&{bless %data1}
,并且存储在此地址的任何数据都将永久丢失。没有办法从脚本中访问存储在该位置的数据。
我的问题是。 。 。 Perl足够聪明,一旦永久丢失对此数据的引用,就会删除存储在&{bless %data1}
中的数据,或者Perl会将这些数据保留在内存中可能导致内存泄漏吗?
答案 0 :(得分:6)
鉴于
package Object {
sub new { my $class = shift; bless({ @_ }, $class) }
}
my $object = Object->new( a => 1, b => 2 );
在第二次作业之前,你有
+============+ +==========+
$object -->[ Reference ----->[ Blessed ]
+============+ [ Hash ]
[ ] +==========+
[ a: --------->[ 1 ]
[ ] +==========+
[ ]
[ ] +==========+
[ b: --------->[ 2 ]
[ ] +==========+
+==========+
(箭头代表指针。)
Perl使用引用计数来确定何时释放变量。作为赋值的一部分,名称(引用)当前引用的变量的引用计数将递减,从而使其被释放 [1] 。这将删除散列的引用计数,导致它被释放 [1] 。这将删除值的引用计数,导致它们被释放 [1] 。
在Perl中,当你有循环引用时会出现内存泄漏。
{
my $parent = Node->new();
my $child = Node->new();
$parent->{children} = [ $child ];
$child->{parent} = $parent;
}
退出街区之前,你有
+-----------------------------------------------------+
| |
+-->+============+ +==========+ |
[ Reference ----->[ Blessed ] |
$parent -->+============+ [ Hash ] |
[ ] +==========+ |
[ children --->[ Array ] |
[ ] [ ] |
+==========+ [ 0: ---------+ |
[ ] | |
+==========+ | |
| |
+--------------------------------------------------+ |
| |
+-->+============+ +==========+ |
[ Reference ----->[ Blessed ] |
$child --->+============+ [ Hash ] |
[ ] |
[ parent: ----------------------+
[ ]
+==========+
存在块之后,你有
+-----------------------------------------------------+
| |
| +============+ +==========+ |
+-->[ Reference ----->[ Blessed ] |
+============+ [ Hash ] |
[ ] +==========+ |
[ children --->[ Array ] |
[ ] [ ] |
+==========+ [ 0: ---------+ |
[ ] | |
+==========+ | |
| |
+--------------------------------------------------+ |
| |
| +============+ +==========+ |
+-->[ Reference ----->[ Blessed ] |
+============+ [ Hash ] |
[ ] |
[ parent -----------------------+
[ ]
+==========+
内存没有被释放,因为所有内容仍然被引用,因为有一个引用周期。由于您无法访问此结构(没有变量名称引用其中的任何内容),因此它是内存泄漏。
答案 1 :(得分:4)
你误解了参数传递的工作方式。 $object
成为新创建的引用,其内容可能会受到传递给构造函数new
的数据的影响,但它不会引用哈希值{{1} }或%data1
自己为%data2
仅提供这些哈希的键/值内容
问题的底线似乎是Perl是否足够聪明,可以在不再使用时解除分配对象,答案是,是的,是的
Perl保留对每个数据项的引用计数,如果它已经降到零(即不再有任何方法可以达到该数据),那么数据被认为可以重用
Perl可能导致内存泄漏的唯一情况是数据结构包含对自身的引用。在这种情况下,外部引用的数量可能会降为零,但数据不会被自己的引用删除,从而使计数不会下降到零
避免包变量也更安全,并且只使用用new
声明的词法变量。当词汇变量超出范围时,它们将自动销毁,因此减少了它们可能包含的任何引用的计数。使用my
声明的包变量将在进程的整个生命周期内存在,并且不会触发此安全措施
如果您再解释一下为什么需要这些信息,那么我相信您会得到更好的答案
答案 2 :(得分:3)
Perl使用一种称为引用计数的方法 - 它计算引用变量的次数。它将数据保存在内存中,直到引用计数降至零。
在您的示例中,一旦重新分配$object
,创建的第一个对象将自动消失。但是有一点需要注意 - 如果在对象中并且new
进程创建了循环引用,则不会发生这种情况。您可以在weaken
中使用Scalar::Util
来处理此问题。
您可以通过创建DESTROY
方法来观察它,该方法在对象被“释放”时调用。
答案 3 :(得分:0)
有引用计数垃圾收集。我的代码中没有看到任何会绊倒的内容。即使有,Scalar :: Util还有其他选项减弱。