-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 =pod I fixed the Angerwhale leak. The main problem was knowing where to look for the leak, but C's C cleared things right up. I ran angerwhale like this: lang:Bash ANGERWHALE_EXIT_OK=1 perl -MDevel::Leak::Object=GLOBAL_bless script/angerwhale_server.pl -d Then I hit it a few hundred times to exaggerate the per-request leaks: ab -n 300 http://localhost:3000/ Then I told it to exit, so I could see a summary of leaks: GET http://localhost:3000/exit This, due to a side-effect of how Catalyst and Devel::Leak::Object interact, prints a summary of leaks: lang:undef Angerwhale::Content::Filesystem::Item 800 Angerwhale::Content::FinalizedItem 800 Angerwhale::Model::Articles 202 Angerwhale::User::Anonymous 800 Scalar::Defer 800 There are actually more lines, but this shows all the classes with more than 100 unfreed instances. The rest are just Catalyst objects that persist throughout the life of the application. Those are fine. Anyway, what you can glean from this is that there's something bad happening with C and Cs in the Articles model. Let's look and see if there's anything that jumps out as suspicious: lang:Perl foreach my $a (@articles) { my $article = $a; # filter kids (lazy) $article->children( lazy { # XXX: a bit messy due to Finalization my @kids = @{$article->children||[]}; $article->{item}{children} = [$self->_apply_filters(@kids)] }); # filter article foreach my $f (@filters) { $article = $f->($article); } push @result, $article; } Well well well. That looks interesting. (C<< $f->($article) >> eventually sets C<$article> to a FinalizedItem, in case you're not familiar with the internals). I reorganized this code to not use C and for C to accept a coderef and do the caching and whatnot that C does for me: sub children { my $self = shift; my $kids = shift; if ($kids) { if (ref $kids eq 'CODE') { $self->{_defer_children} = $kids; } elsif(ref $kids eq 'ARRAY'){ $self->{children} = $kids; } else { croak 'bad data passed to Item::children'; } return; } # if children is an arrayref, return them directly return $self->{children} if ref $self->{children}; if(ref $self->{_defer_children} eq 'CODE'){ my $code = delete $self->{_defer_children}; $self->{children} = [$self->_children] if $self->can('_children'); $self->{children} = $code->($self); return $self->{children}; } return []; } Now I can make the key change: foreach my $a (@articles) { my $article = $a; # filter article foreach my $f (@filters) { $article = $f->($article); } my $format_kids_later = sub { my @kids = @{$_[0]->children||[]}; return [$self->_apply_filters(@kids)]; }; $article->children($format_kids_later); push @result, $article; } The key is that we pass the article to the C<$format_kids_later> subroutine inside C. Now we can use C<$_[0]> instead of C<$article>, which means that we don't close over C<$article> anymore, and the cycle between C<< $article->_item->{_defer_children} >> and C<$article> no longer exists. I didn't actually figure that out myself, I used C to show the cycles affecting C<$article> while I was debugging this. The combination of C (for finding the leaky area in your program) and C (for finding the details of the cycle) is killer. Debugging memory leaks is trivial now! BTW, there is one more leak in C, but caching actually makes that leak not matter much in real life. I will fix it soon anyway, though :) The full details of the Angerwhale change are in the commitdiff L. =cut -----BEGIN PGP SIGNATURE----- Version: GnuPG v1.4.6 (GNU/Linux) iQCVAwUBRtajAdAZeFPdJeQvAQLhWgP/UtF+zLfnWsOW1xBssKe15+ettgzueSXt jhYJPhR5XGJ6r0nfdFntoSd1qDpHbbN/5TbaCW6oehqsCtaaxjXtnq7Hch2Pdybt Y716LvnYOzcaChB8jM962seW4RzYfhlaQZfX7BCxZHeieuM+jbSVe9/RThkUq08t RzyO28fq798= =xx/Z -----END PGP SIGNATURE-----