safsaf32 safsaf32 - 3 months ago 14
Perl Question

Why is the contained object destroyed first?

In the code snippet below, the object of class

Foo
contains a reference to an object of class
Bar
. I would expect that the
Foo
object is destroyed before the
Bar
object. Unfortunately, this doesn't always happen. Strangely enough, I get different behaviors on differenct systems: on my laptop and desktop, the code always runs correctly, whereas on 2 VPSs that I tried, the destructors are ran in reverse order (most of the time). All four systems run the same version of perl (5.20.2 on x86_64 linux).

Also, this only happens when a sub (called
abcd
below) contains a reference to the
Foo
object. If I remove that, the problem goes away.

#!/usr/bin/perl
use strict;
use warnings;

my $foo = Foo->new;

sub abcd {
$foo;
}

####################

package Foo;

sub new {
bless {bar => Bar->new}, 'Foo';
}

sub DESTROY {
my ($self) = @_;
defined $self->{bar} or print "bar is undefined, this should not happen\n";
}

####################

package Bar;

sub new {
bless {}, 'Bar';
}

Answer

When it comes to global destruction, which happens as the program is exiting, perlobj is clear

The order in which objects are destroyed during the global destruction before the program exits is unpredictable.

This is clearly the case without the sub abcd in your posted test program. With the sub, the last reference to the object is inside of the sub so it reaches the global destruction as well. (I also get different behavior in the two cases, but this is meaningless given the above quote.)

Thus in both cases, with or without the sub, the objects are destroyed in an unpredictable order.

When an object is destroyed because the last reference to it went out of scope, things are different. To see such behavior we can add undef $foo; as the last line, to trigger controlled destruction

my $foo = Foo->new;

undef $foo;

END { say "END block." }

sub abcd { $foo; }

This results in Foo being destroyed first, with and without the sub. It also happens before the END block so before the global destruction phase. (Add prints to DESTROY in Foo and Bar.)