覆盖对象时的 Perl 内存管理
Perl memory management when overwriting objects
我的问题是关于 Perl 如何在内部管理对象的数据。
在 Perl 中创建对象时,新的子例程通常会 return 引用受祝福的对象。
以如下代码为例:
# Create a new object
my $object = Object->new(%data1);
# Create a new object with the same variable
$object = Object->new(%data2);
从第一次调用 new
开始,我们创建了一个 $object
引用了一些有福的 %data1
Visual representation:
The "\" symbolizes a reference.
$object ════> \{bless %data1}
This would look as follows in memory:
The "&" symbolizes an address
MEMORY:
----------------------------------
&{bless %data1} ════> bless %data1
然后在第二次调用 new
时 $object
的值被更改为引用其他一些 blessed %data2
Visual representation:
$object ══/ /══> \{bless %data1} # The connection to %data1 is broken
║
╚═══════════> \{bless %data2}
Now memory would look like this:
MEMORY:
----------------------------------
&{bless %data1} ════> bless %data1
&{bless %data2} ════> bless %data2
现在的问题是 $object
不再存储引用 \{bless %data1}
、地址 &{bless %data1}
并且存储在该地址的任何数据都将永远丢失。无法再从脚本访问存储在该位置的数据。
我的问题是。 . .一旦对该数据的引用永远丢失,Perl 是否足够聪明以删除存储在 &{bless %data1}
中的数据,或者 Perl 是否会将该数据保留在内存中可能导致内存泄漏?
有引用计数垃圾回收。我在你的代码中没有看到任何会被绊倒的东西。即使有,在 Scalar::Util 中也有削弱,以及其他选项。
您误解了参数传递的工作方式。 $object
成为一个新创建的引用,其 内容 可能会受到传递给构造函数 new
的数据的影响,但它不会成为对散列的引用 %data1
或 %data2
本身,因为 new
仅给出 key/value 这些散列的内容
你的问题的底线似乎是 Perl 是否足够聪明,可以在对象不再使用时释放它们,答案是肯定的
Perl 保留对每个数据项的引用计数,如果计数降为零(即不再有任何方法可以访问该数据),则认为该数据可用于重用
Perl 可能导致内存泄漏的唯一情况是数据结构包含对自身的引用。在那种情况下,外部引用的数量可能会降为零,但数据会阻止自己被自己的引用删除,从而使计数不会降为零
避免包变量,只使用用 my
声明的词法变量也更安全。词法变量在超出范围时将自动销毁,因此减少了它们可能包含的任何引用的计数。用 our
声明的包变量将在进程的生命周期内存在,并且不会触发此保护措施
如果您多解释一下为什么需要此信息,那么我相信您会得到更好的答案
Perl 使用一种称为引用计数的方法 - 它计算变量被引用的次数。它将数据保存在内存中,直到引用计数降为零。
在您的示例中,创建的第一个对象将在您重新分配 $object
后立即自动消失。但是有一个警告 - 如果 within 你的对象和 new
进程你创建了一个循环引用,这不会发生。您可以在 Scalar::Util
中使用 weaken
来处理这个问题。
您可以通过创建一个DESTROY
方法来观看它,该方法在对象为'freed'时调用。
给出
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 使用引用计数来确定何时释放变量。作为赋值的一部分,当前由名称(Reference)引用的变量的引用计数将被递减,导致它被释放[1]。这将降低散列的引用计数,导致它被释放[1]。这将减少值的引用计数,导致它们被释放[1].
在 Perl 中,当您使用循环引用时会发生内存泄漏。
{
my $parent = Node->new();
my $child = Node->new();
$parent->{children} = [ $child ];
$child->{parent} = $parent;
}
在退出街区之前,您有
$parent +----------------------------------------------------+
| | |
| +============+ +-->+==========+ |
+-->[ Reference -------->[ Blessed ] |
+============+ [ Hash ] |
[ ] +==========+ |
[ children --->[ Array ] |
[ ] [ ] +============+ |
+==========+ [ 0: --------->[ Reference ----+ |
[ ] +============+ | |
+==========+ | |
| |
$child +-------------------------------------------------+ |
| | |
| +============+ +-->+==========+ |
+-->[ Reference -------->[ Blessed ] |
+============+ [ Hash ] |
[ ] +============+ |
[ parent: ---->[ Reference ----------------------+
[ ] +============+
+==========+
存在方块后,你有
+----------------------------------------------------+
| |
+-->+==========+ |
[ Blessed ] |
[ Hash ] |
[ ] +==========+ |
[ children --->[ Array ] |
[ ] [ ] +============+ |
+==========+ [ 0: --------->[ Reference ----+ |
[ ] +============+ | |
+==========+ | |
| |
+-------------------------------------------------+ |
| |
+-->+==========+ |
[ Blessed ] |
[ Hash ] |
[ ] +============+ |
[ parent: ---->[ Reference ----------------------+
[ ] +============+
+==========+
内存没有被释放,因为存在引用循环,所有内容仍在被引用。由于您无法访问此结构(没有变量名引用其中的任何内容),因此这是内存泄漏。
- 假设没有其他引用(指向)这些变量。
我的问题是关于 Perl 如何在内部管理对象的数据。
在 Perl 中创建对象时,新的子例程通常会 return 引用受祝福的对象。
以如下代码为例:
# Create a new object
my $object = Object->new(%data1);
# Create a new object with the same variable
$object = Object->new(%data2);
从第一次调用 new
开始,我们创建了一个 $object
引用了一些有福的 %data1
Visual representation:
The "\" symbolizes a reference.
$object ════> \{bless %data1}
This would look as follows in memory:
The "&" symbolizes an address
MEMORY:
----------------------------------
&{bless %data1} ════> bless %data1
然后在第二次调用 new
时 $object
的值被更改为引用其他一些 blessed %data2
Visual representation:
$object ══/ /══> \{bless %data1} # The connection to %data1 is broken
║
╚═══════════> \{bless %data2}
Now memory would look like this:
MEMORY:
----------------------------------
&{bless %data1} ════> bless %data1
&{bless %data2} ════> bless %data2
现在的问题是 $object
不再存储引用 \{bless %data1}
、地址 &{bless %data1}
并且存储在该地址的任何数据都将永远丢失。无法再从脚本访问存储在该位置的数据。
我的问题是。 . .一旦对该数据的引用永远丢失,Perl 是否足够聪明以删除存储在 &{bless %data1}
中的数据,或者 Perl 是否会将该数据保留在内存中可能导致内存泄漏?
有引用计数垃圾回收。我在你的代码中没有看到任何会被绊倒的东西。即使有,在 Scalar::Util 中也有削弱,以及其他选项。
您误解了参数传递的工作方式。 $object
成为一个新创建的引用,其 内容 可能会受到传递给构造函数 new
的数据的影响,但它不会成为对散列的引用 %data1
或 %data2
本身,因为 new
仅给出 key/value 这些散列的内容
你的问题的底线似乎是 Perl 是否足够聪明,可以在对象不再使用时释放它们,答案是肯定的
Perl 保留对每个数据项的引用计数,如果计数降为零(即不再有任何方法可以访问该数据),则认为该数据可用于重用
Perl 可能导致内存泄漏的唯一情况是数据结构包含对自身的引用。在那种情况下,外部引用的数量可能会降为零,但数据会阻止自己被自己的引用删除,从而使计数不会降为零
避免包变量,只使用用 my
声明的词法变量也更安全。词法变量在超出范围时将自动销毁,因此减少了它们可能包含的任何引用的计数。用 our
声明的包变量将在进程的生命周期内存在,并且不会触发此保护措施
如果您多解释一下为什么需要此信息,那么我相信您会得到更好的答案
Perl 使用一种称为引用计数的方法 - 它计算变量被引用的次数。它将数据保存在内存中,直到引用计数降为零。
在您的示例中,创建的第一个对象将在您重新分配 $object
后立即自动消失。但是有一个警告 - 如果 within 你的对象和 new
进程你创建了一个循环引用,这不会发生。您可以在 Scalar::Util
中使用 weaken
来处理这个问题。
您可以通过创建一个DESTROY
方法来观看它,该方法在对象为'freed'时调用。
给出
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 使用引用计数来确定何时释放变量。作为赋值的一部分,当前由名称(Reference)引用的变量的引用计数将被递减,导致它被释放[1]。这将降低散列的引用计数,导致它被释放[1]。这将减少值的引用计数,导致它们被释放[1].
在 Perl 中,当您使用循环引用时会发生内存泄漏。
{
my $parent = Node->new();
my $child = Node->new();
$parent->{children} = [ $child ];
$child->{parent} = $parent;
}
在退出街区之前,您有
$parent +----------------------------------------------------+
| | |
| +============+ +-->+==========+ |
+-->[ Reference -------->[ Blessed ] |
+============+ [ Hash ] |
[ ] +==========+ |
[ children --->[ Array ] |
[ ] [ ] +============+ |
+==========+ [ 0: --------->[ Reference ----+ |
[ ] +============+ | |
+==========+ | |
| |
$child +-------------------------------------------------+ |
| | |
| +============+ +-->+==========+ |
+-->[ Reference -------->[ Blessed ] |
+============+ [ Hash ] |
[ ] +============+ |
[ parent: ---->[ Reference ----------------------+
[ ] +============+
+==========+
存在方块后,你有
+----------------------------------------------------+
| |
+-->+==========+ |
[ Blessed ] |
[ Hash ] |
[ ] +==========+ |
[ children --->[ Array ] |
[ ] [ ] +============+ |
+==========+ [ 0: --------->[ Reference ----+ |
[ ] +============+ | |
+==========+ | |
| |
+-------------------------------------------------+ |
| |
+-->+==========+ |
[ Blessed ] |
[ Hash ] |
[ ] +============+ |
[ parent: ---->[ Reference ----------------------+
[ ] +============+
+==========+
内存没有被释放,因为存在引用循环,所有内容仍在被引用。由于您无法访问此结构(没有变量名引用其中的任何内容),因此这是内存泄漏。
- 假设没有其他引用(指向)这些变量。