polettix has asked for the wisdom of the Perl Monks concerning the following question:

Esteemed monks,

I always get stuck with memory leak problems when I try to use Perl from C (my very first post here was about this), but this time I can't believe that the problem is due to a bug in perl!

Here's the test code I'm using:
/* Call this file arrays.c, and compile with: gcc -o arrays arrays.c `perl -MExtUtils::Embed -e ccopts -e ldopts` In my system: perl -MExtUtils::Embed -e ccopts -fno-strict-aliasing -pipe -I/usr/local/include -D_LARGEFILE_SOU +RCE -D_FILE_OFFSET_BITS=64 -I/opt/perl/lib/5.8.8/i686-linux/CORE perl -MExtUtils::Embed -e ldopts -Wl,-E -L/usr/local/lib /opt/perl/lib/5.8.8/i686-linux/auto/Dyn +aLoader/DynaLoader.a -L/opt/perl/lib/5.8.8/i686-linux/CORE -lperl -ln +sl -ldl -lm -lcrypt -lutil -lc */ #include <EXTERN.h> #include <perl.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #define ELEMENTS 100000 #define ROUNDS 1000 #define MILESTONE 100 static PerlInterpreter *my_perl; AV* create_av () { AV *av = newAV(); int i; for (i = 0; i < ELEMENTS; ++i) { av_push(av, newSViv(i)); } return av; } void print_memory (int milestone) { FILE *fh; int nread; const int bufsize = 4096; char buffer[bufsize]; printf("\n\n**** %d ****\n", milestone); sprintf(buffer, "/proc/%d/status", getpid()); fh = fopen(buffer, "r"); while ((nread = fread(buffer, 1, bufsize, fh)) > 0) { fwrite(buffer, 1, nread, stdout); } } void cycles () { int i; for (i = 1; i <= ROUNDS; i++) { AV *av = create_av(); if (i % MILESTONE == 0) { print_memory(i); } av_undef(av); } } int main (int argc, char *argv[], char *env[]) { static char *dummy_argv[] = {"","-e","0"}; static int dummy_argc = 3; PERL_SYS_INIT3(&dummy_argc, &dummy_argv, &env); my_perl = perl_alloc(); perl_construct(my_perl); PL_exit_flags |= PERL_EXIT_DESTRUCT_END; perl_parse(my_perl, NULL, dummy_argc, dummy_argv, (char **) NULL); cycles(); perl_destruct(my_perl); perl_free(my_perl); PERL_SYS_TERM(); return 0; }
I'm using this with perl 5.8.8 in Linux. The main() function initialises the Perl stuff. The main objective is doing some cycles(), in which I repeatedly create an array and populate it (create_av()) and then dispose it. At some given intervals I print the memory status (print_memory()). Here's what I got (after filtering out most lines):
**** 100 **** VmSize: 5528 kB **** 200 **** VmSize: 5536 kB **** 300 **** VmSize: 5544 kB **** 400 **** VmSize: 5816 kB **** 500 **** VmSize: 5824 kB **** 600 **** VmSize: 5832 kB **** 700 **** VmSize: 5840 kB **** 800 **** VmSize: 5848 kB **** 900 **** VmSize: 5860 kB **** 1000 **** VmSize: 5868 kB
I tried different values for the constants (ELEMENTS, ROUNDS and MILESTONE), but the trend remains: the more the cycles, the bigger the memory.

Where am I leaking it?

Update: it turned out that I had two leaks: I had to use SvREFCNT_dec(av) instead of av_undef(av) and.. to close the filehandle I used to read the memory usage. Thanks to dave_the_m for spotting them both (what are you waiting? Go vote him!).

Flavio
perl -ple'$_=reverse' <<<ti.xittelop@oivalf

Don't fool yourself.

Replies are listed 'Best First'.
Re: Memory leak dealing with AVs
by dave_the_m (Monsignor) on Sep 22, 2006 at 23:06 UTC
    You undef av, but don't free it. Try replacing av_undef(av) with SvREFCNT_dec(av)

    Dave.

      It helps, but the problem seems to remain:
      ROUND SvREFCNT_dec av_undef 100 5524 5528 200 5528 5536 300 5532 5544 400 5540 5816 500 5544 5824 600 5548 5832 700 5552 5840 800 5556 5848 900 5560 5860 1000 5564 5868

      Flavio
      perl -ple'$_=reverse' <<<ti.xittelop@oivalf

      Don't fool yourself.
        The remaining leak is caused by your instrumentation. Try adding fclose(fh)

        Dave.

Re: Memory leak dealing with AVs
by chromatic (Archbishop) on Sep 22, 2006 at 22:43 UTC

    I haven't compiled your code, but are you leaking the SVs inside the AVs? I can never remember how to free things unless I use the stack macros.