Weak references#
Perl tracks object lifetime with reference counting. Every value knows how many references point at it; when the count drops to zero, the value is freed. This works well for tree-shaped data, where references flow one way, parent to child. It fails for cycles, where a chain of references eventually points back at something earlier in the chain. The cycle’s members keep each other alive even when nothing outside the cycle refers to them any more.
A weak reference is a reference that does not increment the referent’s reference count. It lets you point at something without keeping it alive. Weak references are Perl’s standard answer to reference cycles.
How a cycle leaks#
Consider a doubly-linked list node that holds both next and
prev references:
sub new_node {
my ($value) = @_;
return { value => $value, next => undef, prev => undef };
}
my $a = new_node('A');
my $b = new_node('B');
$a->{next} = $b;
$b->{prev} = $a;
$a and $b each have a reference count of 2:
$ais referenced by the lexical$aand by$b->{prev}.$bis referenced by the lexical$band by$a->{next}.
When the lexicals go out of scope, their contribution drops:
$adrops to count 1 (only$b->{prev}left).$bdrops to count 1 (only$a->{next}left).
Neither reaches zero. Both nodes are unreachable from anywhere in your program, yet they keep each other alive for the life of the interpreter. That’s a leak.
Fixing the leak with weaken#
Scalar::Util::weaken turns an existing reference into a weak
one. The reference still works for dereferencing; it just doesn’t
count toward keeping the target alive.
The standard pattern: whichever direction you consider “back-pointer” or “parent pointer”, make that one weak.
use Scalar::Util qw(weaken);
my $a = new_node('A');
my $b = new_node('B');
$a->{next} = $b;
$b->{prev} = $a;
weaken $b->{prev}; # the back-pointer
Now $a’s reference count stays at 1 (only the lexical $a
contributes; $b->{prev} is weak and does not count). When $a
goes out of scope, $a is freed — and because freeing $a drops
$b->{next}, $b is freed too. The whole structure collapses.
When a weak reference becomes undef#
If the referent is freed while a weak reference still exists, the
weak reference automatically becomes undef. You never see a
dangling pointer:
use Scalar::Util qw(weaken);
my $target = { name => 'alive' };
my $weak = $target;
weaken $weak;
print defined $weak ? "yes\n" : "no\n"; # yes
undef $target; # last strong ref gone
print defined $weak ? "yes\n" : "no\n"; # no
defined $weak is the standard way to check whether the target
is still there. See defined.
Where weak references belong#
The rule: use a weak reference for the “non-owning” pointer in any pair of references that would otherwise cycle.
Doubly-linked list.
nextowns;previs weak.Parent/child tree with parent pointers.
childrenowns;parenton each child is weak.Observer pattern. The subject holds weak references to its observers; observers hold strong references to the subject. When an observer drops its reference, it goes away; the subject’s entry for it turns to
undefand can be swept.Caches that shouldn’t keep entries alive. A cache whose values are weak references does not extend the lifetime of the cached objects.
isweak — inspecting a reference#
Scalar::Util::isweak tells you whether a given scalar holds a
weak reference:
use Scalar::Util qw(weaken isweak);
my $x = { k => 1 };
my $y = $x;
print isweak($y) ? "weak\n" : "strong\n"; # strong
weaken $y;
print isweak($y) ? "weak\n" : "strong\n"; # weak
Useful mostly when debugging a lifetime problem. Production code
rarely branches on isweak.
Common mistakes#
Weakening the wrong end. Weakening the “forward” pointer (the one that owns the successor) frees the successor immediately, before you ever use it. Weak goes on the back-pointer.
Weakening a lexical.
weaken $refwhere$refis a fresh lexical referencing$targetmakes the lexical weak. If nothing else holds a strong reference to$target, it vanishes at the next statement and$refbecomesundef. Weak references belong in data-structure slots, not typically in local lexicals.Assuming weak references are automatic. Perl does not detect cycles and weaken them for you. You have to opt in.
Beyond Scalar::Util#
Scalar::Util ships with Perl; it is always available. For more
elaborate needs — WeakRef, Hash::Util::FieldHash — reach for
CPAN. For ordinary code, weaken is all you need.
Where to go next#
You’ve finished the references tutorial. Good next reads: