Copyright Notice

This text is copyright by CMP Media, LLC, and is used with their permission. Further distribution or use is not permitted.

This text has appeared in an edited form in Perl Journal magazine. However, the version you are reading here is as the author originally submitted the article for publication, not after their editors applied their creativity.

Please read all the information in the table of contents before using this article.

Perl Journal Column 04 (Aug 2003)

[Suggested title: 'Understanding object destructors']

Objects -- they come and go. Most of the time, we're concerned with how they get started, and what they mean while they're around.

But occasionally, we need to know when an object goes away. For example, the object might be holding a filehandle that needs to be closed cleanly, or be using a temporary file that needs to be removed. Or maybe the object is a database transaction that needs to be committed or aborted.

Perl knows at all times how many references are being held for an object (including the built-in primitive types). This is necessary because Perl needs to free the associated memory for that object when the object is no longer needed. For a user-defined object, we can also ask Perl to notify us when the object is removed, so we can attach behavior to that step of the program.

Let's look at an example. Suppose we have objects representing colored boxes. A simple class definition might look like this:

  BEGIN {
    package Box;
    sub colored {
      my $class = shift;
      bless { Color => shift }, $class;
    }
    sub DESTROY {
      my $dead = shift;
      print "$dead->{Color} has been destroyed\n";
    }
  }

We've wrapped this into a BEGIN block to simulate the result of use-ing a module containing this code, while giving us the flexibility to simply include the text within our main program. The colored method is a classic constructor, creating a hashref object with a single key named Color. This key's corresponding value holds the color of the box.

The DESTROY method defines a behavior when a box is finally removed. As with all instance methods, the first parameter is the instance object, which is about to be destroyed. In this case, we're merely documenting the object's demise.

The destructor can be triggered when the last variable holding the object is assigned a different value, when that variable goes out of scope, or when the program ends. Here's a short snippet illustrating all three:

  my $red = Box->colored("red");
  my $green = Box->colored("green");
  print "green is being undef'ed\n";
  $green = undef;
  {
    my $blue = Box->colored("blue");
    print "end of blue block\n";
  }
  print "end of program\n"

The red box is created at the beginning of the program, and persists throughout the program. The green box is held in $green, which is then overwritten with a different value (in this case, undef). The blue box is being held in a variable local to a block, and disappears when the block exits. The output from this program looks like:

  green is being undef'ed
  green has been destroyed
  end of blue block
  blue has been destroyed
  end of program
  red has been destroyed

Note that the red box is destroyed at the very end of the program. All remaining objects are destroyed in an unpredictable order at the end of the program, unless the program is killed by an uncaught signal or a very serious Perl error (called ``panic'' errors), or uses exec.

If the object is held by another object, nested object destruction can occur. Again, the object doesn't go away until its very last reference is removed, so the order of calling destructors for nested objects can be reasonably managed with a little bit of thought and understanding.

Let's put the boxes on a shelf, and see what happens when we get rid of the shelf. First, we'll create a shelf object, including methods to add boxes:

  BEGIN {
    package Shelf;
    sub new {
      bless [], shift;
    }
    sub DESTROY {
      my $dead = shift;
      print "a shelf has been destroyed\n";
    }
    sub add {
      my $self = shift;
      push @$self, @_;
    }
  }

The object is a simple blessed array reference, rather than a hash reference, created with a simple new method. The array is initially empty, reflecting an empty shelf. (I suppose I could have had the constructor also take the remaining arguments as the initial contents, but let's keep things simple.)

The destructor method for a shelf is very brief: merely announcing that the shelf is going away. We'll be updating that method shortly.

The last method deals with the contents. Adding an object involves pushing it onto the end of the (infinite) shelf.

Let's start by creating a shelf, putting a red and blue box onto it, and then destroying the shelf:

  my $shelf = Shelf->new;
  my $red = Box->colored("red");
  my $blue = Box->colored("blue");
  $shelf->add($red, $blue);
  print "destroying the shelf\n";
  $shelf = undef;
  print "end of program\n"

And this generates the output of:

  destroying the shelf
  a shelf has been destroyed
  end of program
  blue has been destroyed
  red has been destroyed

Just before destroying the shelf, each box has two references: the lexical variables of the program ($red and $blue), and the array contents of the shelf itself. When the last reference to the shelf is destroyed, the destructor is called, and then all of the contents are eliminated. Because the two boxes still have a remaining reference, they are not eliminated just yet.

However, if we remove the scalar variables, we get a different behavior:

  my $shelf = Shelf->new;
  {
    my $red = Box->colored("red");
    my $blue = Box->colored("blue");
    $shelf->add($red, $blue);
    print "exiting the block\n";
  }
  print "destroying the shelf\n";
  $shelf = undef;
  print "end of program\n"

Here, we're using $red and $blue only within the block, so when the block exits, those references are removed, but the shelf still contains a single reference to the boxes. Thus, destroying the shelf also destroys the boxes, and we get the following output:

  exiting the block
  destroying the shelf
  a shelf has been destroyed
  blue has been destroyed
  red has been destroyed
  end of program

Note that the blue and red boxes are destroyed immediately after the shelf goes away. With no remaining references, they must be removed. Also note that the destruction is rather automatic. In the absence of additional code, the container class is destroyed before the things it contains, which makes sense when you think about the reference counting that is happening behind the scenes.

What if we wanted the boxes gone before the shelf goes away? We could smarten up the shelf destruction a bit to push the boxes off the shelf before the shelf is gone:

  ## in package Shelf:
  sub DESTROY {
    my $dead = shift;
    while (@$dead) {
      print "shelf has ".@$dead." items\n";
      shift @$dead;
    }
    print "a shelf has been destroyed\n";
  }

Now, with the same code from before, we get:

  exiting the block
  destroying the shelf
  shelf has 2 items
  red has been destroyed
  shelf has 1 items
  blue has been destroyed
  a shelf has been destroyed
  end of program

For every box on the shelf, we shift the contents array, causing the box to fall off the shelf. Because these are the last references to the boxes, the box also suffers a fatal blow, and we get a different behavior.

Thus, by default, we get the container destroyed before any contents, but with a simple bit of coding to walk through the contents and remove them explicitly, we can get the contents to be destroyed first.

Destructors can be handy, but remember that they are invoked only when the last reference has been eliminated. As an example, let's alter the box class so that it keeps track of every box created so that we can iterate over them, and also returns the same identical box if asked for the same color twice (there can be only one red box, that is). We'll do that with a registry of boxes:

  BEGIN {
    package Box;
    my %REGISTRY;
    sub colored {
      my $class = shift;
      my $color = shift;
      $REGISTRY{$color} ||= bless { Color => $color }, $class;
    }
    sub colors {
      sort keys %REGISTRY;
    }
    sub DESTROY {
      my $dead = shift;
      print "$dead->{Color} has been destroyed\n";
      delete $REGISTRY{$dead->{Color}};
    }
  }

The %REGISTRY hash is keyed by the color name, with the value being the box object of that color. As each new box is requested, if the box already exists, it's returned. Otherwise, a new box is created and stored into the hash before being returned. The colors method lets us get at all the current box colors, and we've added a step to the destructor to ensure that the box is removed from our registry.

At first glance, it looks like we've got decent code. Let's add a few lines to our sample program and run it:

  my $shelf = Shelf->new;
  my $orange = Box->colored("orange");
  print "before, we have boxes colored: ", join(", ", Box->colors), "\n";
  {
    my $red = Box->colored("red");
    my $blue = Box->colored("blue");
    my $green = Box->colored("green");
    $shelf->add($red, $blue);
    print "inside, we have boxes colored: ", join(", ", Box->colors), "\n";
    print "exiting the block\n";
  }
  print "outside, we have boxes colored: ", join(", ", Box->colors), "\n";
  print "destroying the shelf\n";
  $shelf = undef;
  print "finally, we have boxes colored: ", join(", ", Box->colors), "\n";
  print "end of program\n"

Note that we've got an orange box at the beginning and a green box inside the block, and we're calling the colors method from time to time to see what all the boxes are like. But let's study the perhaps-unexpected output for a moment:

  before, we have boxes colored: orange
  inside, we have boxes colored: blue, green, orange, red
  exiting the block
  outside, we have boxes colored: blue, green, orange, red
  destroying the shelf
  shelf has 2 items
  shelf has 1 items
  a shelf has been destroyed
  finally, we have boxes colored: blue, green, orange, red
  end of program
  red has been destroyed
  blue has been destroyed
  green has been destroyed
  orange has been destroyed

Everything looks fine up to exiting the block. Why didn't the green box go away? And when the red and blue boxes were pushed off the shelf, why didn't they go away? In fact, nothing goes away until the end of the program!

The problem is in the registry. Because the registry hash holds a reference to every box created until it is destroyed, there's at least one reference to every box as long as the registry is intact. And even though we tried to eliminate that reference in the destructor, the destructor is never called because there's still that last reference!

The solution is to have some way to tell Perl that the links within the registry really aren't as important as the references throughout the rest of the program, and should not count for reference counting. In Perl terminology, this is called a weak reference.

We weaken a reference using Scalar::Util's weaken routine, which is in the core in Perl 5.8, but can be installed on Perl 5.6 or later. The argument to weaken is marked as insignificant to the reference count. If all non-weak references are deleted, the object destructor is called. The remaining weak references are then set to undef automatically.

If we fix the constructor to use weak references, we get:

  ## in package Box:
  use Scalar::Util qw(weaken);
  sub colored {
    my $class = shift;
    my $color = shift;
    return $REGISTRY{$color} if $REGISTRY{$color};
    my $self = bless { Color => $color }, $class;
    weaken ($REGISTRY{$color} = $self);
    $self;
  }

Again, if there's already an existing object of the right color, we re-use it. If not, we create a new object, and copy that object into the registry. The copied reference is then weakened, and we return the object back as the final value. Note that a weak reference makes a normal reference when copied: only the copies specifically marked with weaken are weak. And when we run this, we get output a little more like we expected:

  before, we have boxes colored: orange
  inside, we have boxes colored: blue, green, orange, red
  exiting the block
  green has been destroyed
  outside, we have boxes colored: blue, orange, red
  destroying the shelf
  shelf has 2 items
  red has been destroyed
  shelf has 1 items
  blue has been destroyed
  a shelf has been destroyed
  finally, we have boxes colored: orange
  end of program
  orange has been destroyed

I hope you've enjoyed this little walk through destructors and weak references. For longer examples, be sure to check out my latest book, Learning Perl's Objects, References, and Modules, where I cover this topic in more depth, along with everything else needed to write larger programs. Until next time, enjoy!


Randal L. Schwartz is a renowned expert on the Perl programming language (the lifeblood of the Internet), having contributed to a dozen top-selling books on the subject, and over 200 magazine articles. Schwartz runs a Perl training and consulting company (Stonehenge Consulting Services, Inc of Portland, Oregon), and is a highly sought-after speaker for his masterful stage combination of technical skill, comedic timing, and crowd rapport. And he's a pretty good Karaoke singer, winning contests regularly.

Schwartz can be reached for comment at merlyn@stonehenge.com or +1 503 777-0095, and welcomes questions on Perl and other related topics.