Perl uses a reference counting approach [4] to memory management. Each time an object (scalar, array, hash, etc.) acquires a name or a new reference, Perl increments that object's reference count. Whenever an object loses a name or a reference, Perl decrements its reference count. Once an object's reference count reaches zero, Perl deletes the object and reclaims the storage used by it.
Reference counting fails when objects point to one another in a circular or self-referential fashion. A simple $a = \$a will create a cycle, but let's consider the following more interesting example:
The block inside the main package creates two objects belonging to the class circular , each one containing a reference to the other. The situation looks like this just before the end of the block:
Each object has a reference count of two: one due to the reference from $a / $b and the other due to the reference from the other object. The lexical variables $a and $b go out of scope once the block exits, and then we have the following situation:
Hmm. We can no longer get at these objects, because neither has a name and we have no external references to either of them. They are just taking up space. Unfortunately, there's nothing Perl can do to help us, because both objects still have a reference count of one. These objects will continue to hang around until the entire program exits. Eventually, these objects will be formally destroyed . At the very end of a thread of execution, Perl makes a pass with a " mark-sweep" garbage collector. This final pass will destroy all of the objects created by the interpreter, accessible or otherwise . If you run the example above, you will see the final pass in action: % tryme the end b: nuked a: nuked As you might expect, the objects are destroyed after the interpreter executes the last statement in the normal flow of the program. This final pass is important. Perl can be used as an embedded language. If the interpreter were used repeatedly within the same process to execute code like the above, it would leak memory if there were not a sure-fire means of destroying all the objects created during that thread. There is no way to clean up this mess once you get into it, but you can prevent it by the careful application of brute force. You have to implement a technique for explicitly breaking the circular references. One solution that would work in the case above is the following:
Here we save a link into the circular data structure into the variable $head . Because there is only a single cycle in the structure, breaking a single link is enough to allow Perl to reclaim all the objects in it. If this doesn't seem thorough enough, you can handle them all yourself:
Here we traverse the structure and explicitly destroy every one of the troublesome references. Note that what we are doing is destroying references to the objects we want to delete so that their reference count goes to zero. There is no way to explicitly destroy an object in Perl regardless of its reference countif there were, it could be a horrendous source of bugs and crashes. Another approach is to do the work in two passes , in a fashion somewhat like a mark-sweep collector. First, we acquire a list or "catalog" of the references that need to be destroyed:
This loop traverses the self-referential structure and collects a list of references to all the references that need to be destroyed. The next pass just traverses the list and destroys them: foreach (@refs) { print "preemptive strike on $$_\n"; undef $$_; } A two-pass approach is extravagant in the case of a simple circular list such as this one, but in the case of a graphlike structure containing many cycles, it may be the only alternative. |