覆盖对象时的 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 ----------------------+
                          [          ]   +============+
                          +==========+

内存没有被释放,因为存在引用循环,所有内容仍在被引用。由于您无法访问此结构(没有变量名引用其中的任何内容),因此这是内存泄漏。


  1. 假设没有其他引用(指向)这些变量。