modperl-perl_memory_leak_bug

This is part of The Pile, a partial archive of some open source mailing lists and newsgroups.



To: "Ken Williams" <ken@forum.swarthmore.edu>
From: Alex Krohn <alex@gossamer-threads.com>
Subject: Re: memory leaking with closures
Date: Fri, 07 Sep 2001 00:27:58 -0700

Hi,

> >> With this simple test script:
> >>
> >> print "Content-type: text/html\n\n";
> >> my $var = 'x' x 500000;
> >
> > our $var = 'x' x 500000;
> >
> >>
> >> my $sub = sub { my $sub2 = sub { $var; }; };
> >> print "Done\n";
> >>
> >> $var does not get freed, and the process grows each request. Has anyone
> >> seen this sort of behavior and have any ideas/workarounds (besides don't
> >
> > yes.  this is the documented behavior for closures.  the anonymous sub
> > must have it's own copy of $var which happens to be 500k plus perl
> > overhead on each invocation.
> 
> I think the problem is that $var doesn't get freed even when $var and
> $sub and $sub2 have all gone out of scope.  That's a perl bug for which I
> can fathom no workaround.
> 
> If any of those variables are still in scope, then of course $var can't
> get freed.

Yes, upon further investigation, this perl script:

while (1) {
	{
		my $var = 'x' x 500000;
		my $sub = sub { my $sub2 = sub { $var; } };
	}
	# $var and $sub should be gone, but memory is never freed
	sleep 1; # Don't crash things =)
}

will grow forever as $var never seems to be freed even when everything
should go out of scope, definately a perl bug, not a mod_perl one. =)
Oddly, if you just do 

	my $sub = sub { $var; };

it does not grow, definately something strange going on. Happens on
perl 5.004_04, 5.005_03 and 5.6.1. 

===

To: Alex Krohn <alex@gossamer-threads.com>
From: Stas Bekman <stas@stason.org>
Subject: Re: memory leaking with closures
Date: Fri, 7 Sep 2001 18:16:47 +0800 (SGT)

On Fri, 7 Sep 2001, Alex Krohn wrote:

> Hi,
>
> > >> With this simple test script:
> > >>
> > >> print "Content-type: text/html\n\n";
> > >> my $var = 'x' x 500000;
> > >
> > > our $var = 'x' x 500000;
> > >
> > >>
> > >> my $sub = sub { my $sub2 = sub { $var; }; };
> > >> print "Done\n";
> > >>
> > >> $var does not get freed, and the process grows each request. Has anyone
> > >> seen this sort of behavior and have any ideas/workarounds (besides don't
> > >
> > > yes.  this is the documented behavior for closures.  the anonymous sub
> > > must have it's own copy of $var which happens to be 500k plus perl
> > > overhead on each invocation.
> >
> > I think the problem is that $var doesn't get freed even when $var and
> > $sub and $sub2 have all gone out of scope.  That's a perl bug for which I
> > can fathom no workaround.
> >
> > If any of those variables are still in scope, then of course $var can't
> > get freed.
>
> Yes, upon further investigation, this perl script:
>
> while (1) {
> 	{
> 		my $var = 'x' x 500000;
> 		my $sub = sub { my $sub2 = sub { $var; } };
> 	}
> 	# $var and $sub should be gone, but memory is never freed
> 	sleep 1; # Don't crash things =)
> }
>
> will grow forever as $var never seems to be freed even when everything
> should go out of scope, definately a perl bug, not a mod_perl one. =)

Look like memory leaking to me. The reference count is done incorrectly,
which leads to memory leaking:

use GTop;
use Devel::Peek;
my $gtop = GTop->new;

for (1..3) {
    my $a = 'x' x 500000;
    {
        sub { $a; };
    }
    printf "RERCNT %s: \$a=%d\n",
        GTop::size_string($gtop->proc_mem($$)->rss),
        Devel::Peek::SvREFCNT($a);
}

prints:

RERCNT  3.5M: $a=1
RERCNT  3.5M: $a=1
RERCNT  3.5M: $a=1

all fine!

use GTop;
use Devel::Peek;
my $gtop = GTop->new;

for (1..3) {
    my $a = 'x' x 500000;
    {
        sub { my $sub2 = sub { $a; }; };
    }
    printf "RERCNT %s: \$a=%d\n",
        GTop::size_string($gtop->proc_mem($$)->rss),
        Devel::Peek::SvREFCNT($a);
}

prints:

RERCNT  3.5M: $a=4
RERCNT  4.0M: $a=3
RERCNT  4.5M: $a=3

1. the reference counting is wrong
2. memory leaks (because of 1)

This is a hack to fix the leaking (well, almost):

use GTop;
use Devel::Peek;
my $gtop = GTop->new;

for (1..3) {
    my $a = 'x' x 500000;
    {
        sub { my $sub2 = sub { $a; }; };
        Devel::Peek::SvREFCNT_dec($a);
        Devel::Peek::SvREFCNT_dec($a);
    }
    printf "RERCNT %s: \$a=%d\n",
        GTop::size_string($gtop->proc_mem($$)->rss),
        Devel::Peek::SvREFCNT($a);
}

prints:

RERCNT  3.5M: $a=2
RERCNT  4.0M: $a=1
RERCNT  4.0M: $a=1


> Oddly, if you just do
>
> 	my $sub = sub { $var; };
>
> it does not grow, definately something strange going on. Happens on
> perl 5.004_04, 5.005_03 and 5.6.1.

You mean this works for you?:

while (1) {
       {
               my $var = 'x' x 500000;
               my $sub = sub { $var; };
       }
       # $var and $sub should be gone, but memory is never freed
       sleep 1; # Don't crash things =)
}

that's because your $sub goes out of scope. the other one seems like a
bug.

If I didn't miss something, it seems that we need to run this through p5p.

===

To: "Stas Bekman" <stas@stason.org>, "Alex Krohn"
<alex@gossamer-threads.com>
From: "Perrin Harkins" <perrin@elem.com>
Subject: Re: memory leaking with closures
Date: Fri, 7 Sep 2001 10:23:38 -0400

> > Oddly, if you just do
> >
> > my $sub = sub { $var; };
> >
> > it does not grow, definately something strange going on. Happens on
> > perl 5.004_04, 5.005_03 and 5.6.1.
>
> You mean this works for you?:
>
> while (1) {
>        {
>                my $var = 'x' x 500000;
>                my $sub = sub { $var; };
>        }
>        # $var and $sub should be gone, but memory is never freed
>        sleep 1; # Don't crash things =)
> }
>
> that's because your $sub goes out of scope.

The leaking behavior only happens when you use nested anonymous subs.  It
should always be possible to avoid it, although your code may look uglier.

> the other one seems like a
> bug.
>
> If I didn't miss something, it seems that we need to run this through p5p.

It's been known about for a while.  I'm not sure what the status of getting
a fix is.  I assume that it must be difficult to fix or it would have been
changed a long time ago.

===

To: Perrin Harkins <perrin@elem.com>
From: Stas Bekman <stas@stason.org>
Subject: Re: memory leaking with closures
Date: Fri, 7 Sep 2001 22:47:56 +0800 (SGT)

On Fri, 7 Sep 2001, Perrin Harkins wrote:

> > > Oddly, if you just do
> > >
> > > my $sub = sub { $var; };
> > >
> > > it does not grow, definately something strange going on. Happens on
> > > perl 5.004_04, 5.005_03 and 5.6.1.
> >
> > You mean this works for you?:
> >
> > while (1) {
> >        {
> >                my $var = 'x' x 500000;
> >                my $sub = sub { $var; };
> >        }
> >        # $var and $sub should be gone, but memory is never freed
> >        sleep 1; # Don't crash things =)
> > }
> >
> > that's because your $sub goes out of scope.
>
> The leaking behavior only happens when you use nested anonymous subs.  It
> should always be possible to avoid it, although your code may look uglier.

It only happens if you use double-nested subs, as far as I've tested with
Devel::Peek. Is it documented somewhere in the perl docs?

> > the other one seems like a
> > bug.
> >
> > If I didn't miss something, it seems that we need to run this through p5p.
>
> It's been known about for a while.  I'm not sure what the status of getting
> a fix is.  I assume that it must be difficult to fix or it would have been
> changed a long time ago.

Do you say that it would be a waste to raise this issue on p5p?

It's very hard to keep up with the traffic on that list along with
dev@httpd.apache.org, so I'm not aware of many issues discussed there.
unfortunately :(

===

To: Alex Krohn <alex@gossamer-threads.com>
From: Barrie Slaymaker <barries@slaysys.com>
Subject: Re: memory leaking with closures
Date: Fri, 7 Sep 2001 10:50:13 -0400

On Fri, Sep 07, 2001 at 12:27:58AM -0700, Alex Krohn wrote:
> while (1) {
> 	{
> 		my $var = 'x' x 500000;
> 		my $sub = sub { my $sub2 = sub { $var; } };
> 	}
> 	# $var and $sub should be gone, but memory is never freed
> 	sleep 1; # Don't crash things =)
> }
> 
> will grow forever

> it does not grow, definately something strange going on. Happens on
> perl 5.004_04, 5.005_03 and 5.6.1. 

confirmed in bleadperl (patch 11936).  CCing p5p.

atleast one sub { sub{} } leak was fixed recently, but not this one.

===

To: "Arthur Bergman" <arthur@contiller.se>
From: Gurusamy Sarathy <gsar@ActiveState.com>
Subject: Re: memory leaking with closures 
Date: Fri, 07 Sep 2001 11:00:30 -0700

On Fri, 07 Sep 2001 18:06:17 +0200, "Arthur Bergman" wrote:
>> On Fri, Sep 07, 2001 at 12:27:58AM -0700, Alex Krohn wrote:
>> > while (1) {
>> > {
>> > my $var = 'x' x 500000;
>> > my $sub = sub { my $sub2 = sub { $var; } };
>> > }
>> > # $var and $sub should be gone, but memory is never freed
>> > sleep 1; # Don't crash things =)
>> > }
>> > 
>> > will grow forever
>> 
>> > it does not grow, definately something strange going on. Happens on
>> > perl 5.004_04, 5.005_03 and 5.6.1. 
>> 
>> confirmed in bleadperl (patch 11936).  CCing p5p.
>> 
>> atleast one sub { sub{} } leak was fixed recently, but not this one.
>> 
>> - Barrie
>
>Seems like we are not properly freeing the prototype CV which is cloned.

Not likely, since there are always a fixed number of closure prototypes
when there is no eval"" to create new ones.

It is more likely that the reference loop between the inner and outer
CVs is preventing the freeing of either of them.  I'm not in fact sure
that the reference loop *can* be eliminated trivially, given these two
CVs can have different lifetimes.  Perhaps the right solution is to
move to using weakrefs for CvOUTSIDE(), I dunno.


===

To: "Barrie Slaymaker" <barries@slaysys.com>,
From: "Arthur Bergman" <arthur@contiller.se>
Subject: RE: memory leaking with closures
Date: Fri, 7 Sep 2001 18:06:17 +0200

> On Fri, Sep 07, 2001 at 12:27:58AM -0700, Alex Krohn wrote:
> > while (1) {
> > {
> > my $var = 'x' x 500000;
> > my $sub = sub { my $sub2 = sub { $var; } };
> > }
> > # $var and $sub should be gone, but memory is never freed
> > sleep 1; # Don't crash things =)
> > }
> > 
> > will grow forever
> 
> > it does not grow, definately something strange going on. Happens on
> > perl 5.004_04, 5.005_03 and 5.6.1. 
> 
> confirmed in bleadperl (patch 11936).  CCing p5p.
> 
> atleast one sub { sub{} } leak was fixed recently, but not this one.
> 
> - Barrie

Seems like we are not properly freeing the prototype CV which is cloned.

===

To: "Gurusamy Sarathy" <gsar@ActiveState.com>
From: "Arthur Bergman" <arthur@contiller.se>
Subject: SV: memory leaking with closures 
Date: Fri, 7 Sep 2001 20:18:28 +0200

> On Fri, 07 Sep 2001 18:06:17 +0200, "Arthur Bergman" wrote:
> >> On Fri, Sep 07, 2001 at 12:27:58AM -0700, Alex Krohn wrote:
> >> > while (1) {
> >> > {
> >> > my $var = 'x' x 500000;
> >> > my $sub = sub { my $sub2 = sub { $var; } };
> >> > }
> >> > # $var and $sub should be gone, but memory is never freed
> >> > sleep 1; # Don't crash things =)
> >> > }
> >> > 
> >> > will grow forever
> >> 
> >> > it does not grow, definately something strange going on. Happens on
> >> > perl 5.004_04, 5.005_03 and 5.6.1. 
> >> 
> >> confirmed in bleadperl (patch 11936).  CCing p5p.
> >> 
> >> atleast one sub { sub{} } leak was fixed recently, but not this one.
> >> 
> >> - Barrie
> >
> >Seems like we are not properly freeing the prototype CV which is cloned.
> 
> Not likely, since there are always a fixed number of closure prototypes
> when there is no eval"" to create new ones.

Silly me.

> It is more likely that the reference loop between the inner and outer
> CVs is preventing the freeing of either of them.  I'm not in fact sure
> that the reference loop *can* be eliminated trivially, given these two
> CVs can have different lifetimes.  Perhaps the right solution is to
> move to using weakrefs for CvOUTSIDE(), I dunno.
> 

Sounds like an idea anyway. I will give it a try. Only
problem I see it will be messed up after perl_clone.


===


the rest of The Pile (a partial mailing list archive)

doom@kzsu.stanford.edu