in reply to memory leak when using tail recursion?

I ran into a memory leak in a system I'm working on

I see no leekage. Memory usage is more or less constant.

#!/usr/bin/perl use strict; use warnings; sub bar { if ( foo() ) { goto &bar; } return; } my $i; sub foo { return $i++ < 500_000; } $i=0; bar(); print("Press Enter to continue"); <STDIN>; # 32,824K $i=0; bar(); print("Press Enter to continue"); <STDIN>; # 32,872K $i=0; bar(); print("Press Enter to continue"); <STDIN>; # 32,860K $i=0; bar(); print("Press Enter to continue"); <STDIN>; # 32,856K

The stack and all lexical variables should be deallocated when goto is called, right?

Should? Dunno. Does? No. ( That's why the memory usage increases for each recursive call to bar. )

Replies are listed 'Best First'.
Re^2: memory leak when using tail recursion?
by Limbic~Region (Chancellor) on Feb 14, 2007 at 00:07 UTC
    ikegami,
    What version of perl? I ask because I see the memory leak locally with perl -v
    This is perl, v5.8.8 built for MSWin32-x86-multi-thread (with 25 registered patches, see perl -V for more detail) Copyright 1987-2006, Larry Wall Binary build 817 [257965] provided by ActiveState http://www.ActiveSta +te.com Built Mar 20 2006 17:54:25

    Cheers - L~R

      The exact same build.

      The memory usage does go up for each recursive call to bar (which is why I had to lower the max value for $i), but it does get reclaimed.

        ikegami,
        Very interesting. I am up over 200MB in 30 seconds if I use the block form of if. If I use the post modifier form of if, it stays fixed at 2MB.

        Cheers - L~R

Re^2: memory leak when using tail recursion?
by juddhuck (Acolyte) on Feb 15, 2007 at 19:42 UTC
    I should clarify. The memory does eventually get reclaimed, so this isn't a true memory leak. The memory is just tied up until the final recursive call returns. If the value returned by foo() is large, or if there a large number of recursive calls, then this can cause the system to run out of memory.

    I originally ran into this problem when using Exception::Class to handle errors. I had an exception that caused a tail-recursive call to the current function, and this exception was happening repeatedly because there were a lot of invalid records in a row. Exception::Class objects are pretty large, so the system eventually ran out of memory. The code looked something like this:

    sub get { my ($self) = @_; my ($record, $e); eval { $record = $self->create_record(); }; if ( $e = Exception::Record::Corrupt->caught() ) { # We got a recoverable error, so skip this record. goto &get; } elsif ( $e = Exception::Class->caught() ) { # Rethrow any generic exception caught and abort. ref $e ? $e->rethrow : die $e; } return $record; }
    Anyway, there are plenty of ways to fix this problem. The easiest is to not use tail-recursion, but it *should* work with tail recursion. The odd thing to me is that this problem only occurs when the function call is in the if-conditional.