嵌套闭包和捕获的变量

时间:2015-03-15 19:18:59

标签: perl memory-leaks closures circular-reference

我有这个带有嵌套闭包的例子,它演示了内存泄漏

use v5.10;
use strict;

package Awesome;

sub new {
    bless {steps => [], surprise => undef}, shift;
}

sub say {
    print "awesome: ", $_[1], "\n";
}

sub prepare {
    my ($self, @steps) = @_;

    for my $s (@steps) {
        push @{$self->{steps}}, sub {
            $self->say($s);

            if ($s eq 'pony') {
                $self->{surprise} = sub {
                    $s;
                }
            }
        };
    }
}

sub make {
    my $self = shift;

    while (my $step = shift @{$self->{steps}}) {
        $step->();
    }

    if ($self->{surprise}) {
        printf("And you have surprise: %s\n", $self->{surprise}->());
    }
}

sub DESTROY {
    warn "destroying";
}

package main;

my $a = Awesome->new;
$a->prepare('barbie','pony','flash');
$a->make();

我的perl上的输出是

awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
destroying at /tmp/t.pl line 43 during global destruction.

这个“在全局破坏期间”意味着对象不能以正常方式被破坏,因为它有一些循环引用。

然而,唯一的循环引用是由

创建的
push @{$self->{steps}}, sub {
            $self->say($s);

我们在第一次关闭时使用$ self。然后在make()里面我们将删除这些步骤和循环引用。但看起来这个嵌套的封闭与“惊喜”会产生问题。例如,如果我们不将“pony”传递给prepare(),那么输出将如预期的那样好:

awesome: barbie
awesome: flash
destroying at /tmp/t.pl line 43.

那么,perl中的嵌套闭包是否捕获了与已经捕获的上层闭包相同的变量,即使我们没有使用它们?

1 个答案:

答案 0 :(得分:8)

之前Perl曾经在嵌套的闭包中过度捕获,但自5.18以来它没有这样做。

$ tail -n 9 a.pl   # Modified to make clearer when the object is destroyed.
package main;

{
   my $a = Awesome->new;
   $a->prepare('barbie','pony','flash');
   $a->make();
}

print "done.\n";

$ 5.16.3t/bin/perl a.pl
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
done.
destroying at a.pl line 43 during global destruction.

$ 5.18.2t/bin/perl a.pl
awesome: barbie
awesome: pony
awesome: flash
And you have surprise: pony
destroying at a.pl line 43.
done.