Subject: | Extensive memory usage when numerous exceptions are generated in eval()s |
Date: | Tue, 16 Sep 2014 13:16:48 +0300 |
To: | bug-html-mason [...] rt.cpan.org |
From: | Dean West <dwest1975 [...] ukr.net> |
Hi,
I'm using HTML::Mason version 1.48 and noticed apparent signs of a memory leak, when one of my components was performing a prolonged operation. After tracking the dependencies of my component I was able to reproduce the leak using the following standalone script:
#!/usr/bin/env perl
use strict; use warnings;
use HTML::Mason::Exceptions;
$SIG{'__DIE__'} = \&rethrow_exception;
{ package Foo;
use Carp (); use Class::Std;
sub process { my $self = shift; eval { Carp::confess('process'); }; } }
memory_usage(); for (1 .. 10) { for (1 .. 1000) { my $foo = Foo->new(); $foo->process(); } memory_usage(); }
sub memory_usage { system("ps uw --no-headers -p $$") }
Show quoted text
> ./test_mason.pl 503 14654 0.0 0.2 39012 4496 pts/5 S+ 09:57 0:00 perl ./test_mason.pl 503 14654 31.0 0.3 41472 6948 pts/5 S+ 09:57 0:00 perl ./test_mason.pl 503 14654 63.0 0.4 43880 9404 pts/5 S+ 09:57 0:00 perl ./test_mason.pl 503 14654 94.0 0.6 46360 11912 pts/5 S+ 09:57 0:00 perl ./test_mason.pl 503 14654 61.5 0.7 48840 14320 pts/5 S+ 09:57 0:01 perl ./test_mason.pl 503 14654 75.5 0.8 51236 16800 pts/5 S+ 09:57 0:01 perl ./test_mason.pl 503 14654 89.5 1.0 53828 19400 pts/5 S+ 09:57 0:01 perl ./test_mason.pl 503 14654 69.3 1.1 56288 21752 pts/5 S+ 09:57 0:02 perl ./test_mason.pl 503 14654 79.3 1.2 58620 24156 pts/5 S+ 09:57 0:02 perl ./test_mason.pl 503 14654 88.6 1.3 61092 26560 pts/5 S+ 09:57 0:02 perl ./test_mason.pl 503 1465
4 98.3 1.5 63688 29108 pts/5 S+ 09:57 0:02 perl ./test_mason.pl
The reason behind such memory usage seems to be the way rethrow_exception() sub checks if an object or a class thrown supports rethrow() sub. It uses UNIVERSAL::can() to achieve this goal. In my case the exception is just a scalar containing a backtrace. So far so good if we throw Class::Std out of the equation as the "leak" occurs only if Class::Std is involved. After checking its sources I found out, that it wraps system's UNIVERSAL::can() and does additional internal hierarchy lookups. And this was where the memory went: new hierarchy entries were created for each backtrace.
I understand, that this might be better addressed in Class::Std, but it seems a bit undermaintained at the moment (last release ~5 years ago). So would it be possible to change the way rethrow_exception() sub operates? I suggest the following changes:
diff -u /usr/share/perl5/vendor_perl/HTML/Mason/Exceptions.pm Exceptions.pm
--- /usr/share/perl5/vendor_perl/HTML/Mason/Exceptions.pm 2014-09-01 16:50:14.000000000 +0000
+++ Exceptions.pm 2014-09-15 12:19:38.231780200 +0000
@@ -120,11 +120,13 @@
my ($err) = @_;
return unless $err;
- if ( UNIVERSAL::can($err, 'rethrow') ) {
- $err->rethrow;
- }
- elsif ( ref $err ) {
- die $err;
+ if ( ref $err ) {
+ if ( UNIVERSAL::can($err, 'rethrow') ) {
+ $err->rethrow;
+ }
+ else {
+ die $err;
+ }
}
HTML::Mason::Exception->throw(error => $err);
}
I.e. check the ability to ' rethrow ' only for references (most likely - objects).
TIA
-- Dean