Skip Menu |

This queue is for tickets about the HTML-Mason CPAN distribution.

Report information
The Basics
Id: 98924
Status: rejected
Priority: 0/
Queue: HTML-Mason

People
Owner: Nobody in particular
Requestors: dwest1975 [...] ukr.net
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



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
Subject: [rt.cpan.org #98924] Extensive memory usage when numerous exceptions are generated in eval()s
Date: Tue, 16 Sep 2014 13:24:21 +0300
To: bug-HTML-Mason [...] rt.cpan.org
From: Dean West <dwest1975 [...] ukr.net>
Hi, Re-sending as a plain-text (forgot about the RT tricks): 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 14654 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
On Tue Sep 16 06:24:30 2014, dwest1975@ukr.net wrote: Show quoted text
> Hi, > > Re-sending as a plain-text (forgot about the RT tricks): > > 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 $$") } >
> > ./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 14654 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).
This really seems like something that should be fixed in Class::Std. I'm sure many modules are likely to trigger that problem since ->can is used all over the place in Perl.