Test2:...fcount(3perl)
π Test2::Tools::Refcount π
okay bestie, so this is a page from the Perl Programmers Reference Guide β it's Test2::Tools::Refcount and it's literally here to stop your objects from haunting your program after they should've been yeeted into the void π«Ά
β¨ NAME β¨
"Test2::Tools::Refcount" β assert reference counts on objects. that's it, that's the whole vibe π
πΈ SYNOPSIS πΈ
use Test2::Tools::Refcount;
use Some::Class;
my $object = Some::Class->new();
is_oneref( $object, '$object has a refcount of 1' );
my $otherref = $object;
is_refcount( $object, 2, '$object now has 2 references' );
π¦ DESCRIPTION π¦
so the Perl garbage collector is out here doing plain old reference counting while your program just runs normally. which means cycles, or references somewhere in your code that never got weakened, can keep an object alive way longer than you ever intended. not cute. π
to dodge that whole situation, the reference count of a brand new object β freshly returned by its class constructor β ought to be 1. that way the caller can just trust that when they drop all their references to it, the object gets properly DESTROYed. main character cleanup energy π
this module hands you two test functions so you can make sure that property actually holds for an object class, so you're being polite to your callers. slay.
if the assertion fails β like, the actual reference count is different from what you expected β either of the following two modules can be used to help the developer figure out where all those references are chilling:
- if Devel::MAT is installed, this test module will use it to dump the state of the memory after a failure. it will create a
.pmatfile named the same as the unit test, but with the trailing.tsuffix replaced with-TEST.pmatwhere"TEST"is the number of the test that failed (in case there was more than one). iconic detective work π
peep the examples below for more info.
π FUNCTIONS π
π is_refcount
is_refcount( $object, $count, $name )
test that $object has $count references to it. no cap.
π is_oneref
is_oneref( $object, $name )
assert that the $object has only 1 reference to it. just the one. one singular ref. periodt.
β refcount
$count = refcount( $object )
returns the reference count of the given object as used by the test functions. this is super useful for making tests that don't care what the count is before they start β they just assert that the count hasn't changed by the end. chef's kiss π§βπ³
use Test2::Tools::Refcount import => [qw( is_refcount refcount )];
{
my $count = refcount( $object );
do_something( $object );
is_refcount( $object, $count, 'do_something() preserves refcount' );
}
π EXAMPLE π
imagine you just wrote a brand new class called "MyBall" and now you wanna check that its constructor and methods are well-behaved and aren't leaking references everywhere like a broken faucet. consider this test script bestie:
use Test::More tests => 2;
use Test2::Tools::Refcount;
use MyBall;
my $ball = MyBall->new();
is_oneref( $ball, 'One reference after construct' );
$ball->bounce;
# Any other code here that might be part of the test script
is_oneref( $ball, 'One reference just before EOF' );
the first assertion is right after the constructor, to check that the reference it returned is the only reference to that object. this fact matters a whole lot if we ever want "DESTROY" to behave properly. the second call is right at the end of the file, just before the main scope closes. at that stage we expect the reference count to also be one, so the object gets properly cleaned up. β¨
now suppose, when run, this produces the following output (presuming Devel::MAT::Dumper is available):
1..2
ok 1 - One reference after construct
not ok 2 - One reference just before EOF
# Failed test 'One reference just before EOF'
# at ex.pl line 26.
# expected 1 references, found 2
# SV address is 0x55e14c310278
# Writing heap dump to ex-2.pmat
# Looks like you failed 1 test of 2.
not me getting a whole ex-2.pmat file written out π we can load that bad boy using the "pmat" shell and run the "identify" command on the given address to find where it went:
$ pmat ex-2.pmat
Perl memory dumpfile from perl 5.28.1 threaded
Heap contains 25233 objects
pmat> identify 0x55e14c310278
HASH(0)=MyBall at 0x55e14c310278 is:
ββ(via RV) the lexical $ball at depth 1 of CODE() at 0x55e14c3104a0=main_cv, which is:
β ββthe main code
ββ(via RV) value {self} of HASH(2) at 0x55e14cacb860, which is (*A):
ββ(via RV) value {cycle} of HASH(2) at 0x55e14cacb860, which is:
itself
(quick note: this document is not trying to be a full tutorial on Devel::MAT and the "pmat" shell β for that, go see Devel::MAT::UserGuide π«Ά)
from this output, we can see the constructor was well-behaved, but a reference got leaked by the end of the script β the reference count was 2, when we expected just 1. reading the trace output, we can see there were 2 references that could be found β one stored in the $ball lexical in the main program, and one stored in a HASH. since we expected to find the $ball lexical variable, we know we're now looking for a leak in a hash somewhere in the code. from reading the test script, we can guess this leak is likely in the bounce() method. and on top of that, we know the reference to the object will be stored in a HASH in a member called "self".
by reading the code that implements the bounce() method, we can see this is indeed the case:
sub bounce
{
my $self = shift;
my $cycle = { self => $self };
$cycle->{cycle} = $cycle;
}
from reading the tracing output, we find that the HASH this object is referenced in also contains a reference to itself, in a member called "cycle". that comes from the last line in this function β a line that purposely created a cycle, to demonstrate the point. a real program probably wouldn't do anything quite this obvious, but the trace would still be useful in finding the likely cause of the leak. lowkey forensic genius π
if "Devel::MAT::Dumper" is not available, then those detailed traces will not be produced. the basic reference count testing will still take place, but a smaller message gets produced instead:
1..2
ok 1 - One reference after construct
not ok 2 - One reference just before EOF
# Failed test 'One reference just before EOF'
# at demo.pl line 16.
# expected 1 references, found 2
# Looks like you failed 1 test of 2.
π BUGS π
- Temporaries created on the stack
code which creates temporaries on the stack β to be released again when the called function returns β does not work correctly on perl 5.8 (and probably before). examples such as
is_oneref( [] );
may fail and claim a reference count of 2 instead. rude. π
passing a variable such as
my $array = [];
is_oneref( $array );
works fine. and because of the whole intention of this test module β that is, to assert reference counts on some object stored in a variable during the lifetime of the test script β this is unlikely to cause any problems. so it's giving "known quirk, not a crisis" π
π«Ά ACKNOWLEDGEMENTS π«Ά
Peter Rabbitson <ribasushi@cpan.org> β for suggesting using core's "B" instead of "Devel::Refcount" to obtain refcounts. absolute legend, thank you for your service π
π AUTHOR π
Paul Evans <leonerd@leonerd.org.uk>
and that's the whole page bestie!! this was perl v5.44.0, dated 2026-10-17, filed under Test2:...fcount(3perl) β go forth and count your references responsibly, ilysm πβ¨πΈ
π¦ where this page comes from
- arch rolling: perl 5.44.0-1 Β· Artistic-1.0-Perl AND GPL-1.0-or-later
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms