[Date Prev][Date Next][Thread Prev][Thread Next] [Search] [Date Index] [Thread Index]

Re: [MacPerl] Memory problems in OO context



On Sun, 5 Dec 1999, Ken Williams wrote:

> Nicholas.G.Thornton@directory.reed.edu (Nicholas G. Thornton) wrote:
> >I have been noticing memory leaks laetely. Not so bad as some of my
> >other programs (which are in beta afterall), but still there.
> >Noticeable after a few days of running. Arguably could be the scripts
> >I'm running. Arguably.
> 
> Well, the rules for yelling "memory leak" in a crowded theatre remain
> the same: whittle your code down to the smallest possible program that
> demonstrates the leak, then submit a bug report.  More likely what will
> happen is that you'll discover where your code isn't cleaning up
> properly.  Sure, Perl usually cleans up after you, but sometimes it
> can't.

Good advice.  I've spent the day, partly whittling code, but mostly
watching my machine reboot.  All hail protected memory!  

The first, and probably easiest to fix, problem I've noticed:  why does
AutoLoader come up when I call exit(1) in my program?

I apologize to those who are tired of seeing me post.  I'm tired of
posting, so I know others are bound to be sick of my posts.  I hope to
find some resolution soon, and return to a state of blissful awe at the
creativity and productivity of Alan Fry and Chris Nandor and many others.

Here's what I've found in my memory travels/travails.

This is the main body of code:

  for ($elem = 0; $elem < 250; $elem++)
  {
        # print every third country name
      print "$elem...Afghanistan again...\n";
    
      $info{ "$elem" }{'data'} = GetOneCountry( "af.html" );
      
      # Give MacPerl time off to respond to cancels and mouse clicks
      sleep 1;
  }

  Storable::nstore \%info, 'slurp.store';

  undef %info;

  die "Finishing storage and shutting down.\n";


One interesting thing I've learned is that Storable takes an amount of
memory roughly equal to the size of the file it writes.  So I've got to
have at least twice as much memory as this data structure or file size.
For this test, 'slurp.store' ended up as 5,258,651 bytes.

Another interesting thing is that 'undef %info;' doesn't free up a
boatload of memory like I'd hoped.  Before I inserted the die statement, I
tried retrieving the data structure into an entirely different variable:

  $iref = Storable::retrieve( 'slurp.store' );

This, in combination with the amount of memory used for the first store,
together with the memory for all the iterations, was too much and MacPerl
crashed, taking the OS with it.  For those who care, MacPerl crashed in
_pool_find_ptr_bucket (actually several times during the day), and MacsBug
StdLog got no further than the header.  Where the ROM version normally
shows, MacsBug sez "Unable to access that address".  Blammo.

Anyway, back to the code.  Clearly all the non-trivial work is done in the
GetOneCountry sub, so here it is:

	sub GetOneCountry
	{
	    my( $uri ) = @_;
	    
	    my $p;
	    $p = OneCountryGrabber->new->parse_file("$uri");
	    
	    if ( defined($p))
	    {
	          # try to clear up some memory
	        $theInfo = \%{$p->{metaData}};
	        $p->{metaData} = undef;
	        undef $p->{metaData};
	          # this is the big memory hog
	        undef $p;
	        return $theInfo;
	    } 
	    else 
	    {
	        print "Bad luck this time\n";
	        die   "Parse_file failed."; 
	    }	
	}

Notice all the feeble efforts to slash, undef, clear up, or otherwise
scavenge some memory.  Futile.

If you are wondering about OneCountryGrabber, it is a subclass of
HTML::Filter.  I haven't included it because it isn't so small as these
two functions, and because I think the problem is outside it.  If there's
demand, I'll post it too.

Now, for the stats on memory usage, which I gathered using Metrowerks
ZoneRanger.  There was some variance between runs, but within reason. 
 After opening the source in Perl and running a syntax check on it,
MacPerl's memory looked like this:

      Blocks          Used          Free          Ptrs         Handles
         324           291            33           159             132
  33,952,928     2,228,832    31,724,096     2,109,328         119,504

after running the 250-loop, but before the Storable (I scribbled this down
once, when I had a sleep statement in the code).  I didn't write down the
totals, but you can figure them from Used+Free:

      Blocks          Used          Free          Ptrs         Handles
           ?          1430            14          1272             158
           ?    24,428,432     9,524,496    24,300,016         128,416

After the 250-loop and the Storable, this is how things looked:

      Blocks          Used          Free          Ptrs         Handles
        1700          1659            41          1508             151
  35,317,344    29,973,472     5,343,872    29,836,864         136,608


Clearly memory fragmentation is not my main problem.

It seems to me $p is not freed in sub GetOneCountry, despite all my
efforts.  Nor does it seem to get freed even after deleting %info in the
main function, despite %info being the last possible reference to it.
(Though the data structure is several levels nested, ultimately all its
values are strings.  It does not refer to itself.)

Why doesn't _every_ module returning a reference to something leak memory?

Originally I didn't use $theInfo in GetOneCountry at all, I just returned
\%{$p->{metaData}}.  I thought maybe $theInfo would allow me to clear the
metaData hashref, which in turn would allow $p to be freed.  It didn't
happen that way.

I guess I need to copy the metaData element-by-element:  a deep copy,
rather than just copying the reference.  Hopefully by not using the
reference, when I delete it, it will die as it should.  I'm going to try
Ref->copyref next.

Any thoughts?


--
MattLangford 




# ===== Want to unsubscribe from this list?
# ===== Send mail with body "unsubscribe" to macperl-request@macperl.org