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

Dear monks,

My first post here and I am afraid my question might be so unwise that I look silly but here goes. The members of this monastery have seemed during my many lurking years as being benevolent and kind so maybe I will not be ordered to serve too harsh a penance...

Update - the memory problem occurs even if I copy the arrays contrary to my this post, as described at the end.

Anyway... I'm using Chart clicker to make quite a few graphs (of network stats). Im having an out of memory exception half way through my list of graphs and as far as I thought it should not happen. I am passing in references to arrays to the function which creates the chart objects and I thought when the function returned all associated variables which were created inside the function would be freed but it seems not. If I dereference and copy the array the out of memory problem does not occur. Anyway its probably better for me to show some code which reproduces the problem, as I am not old and wise enough to be able to talk that much more about what I expect to happen and what exactly I am doing. Perhaps there is a simple obvious 'unrelated to chart clicker' action I am taking which I have gotten away with for all these years I have been using perl, or perhaps theres a less obvious reason, but the code will I am sure speak for itself far clearer than I ever can.
#!/usr/bin/perl -s -w use Chart::Clicker; use Chart::Clicker::Data::Series; use Chart::Clicker::Data::DataSet; $| = 1; sub mysub { ($x, $y) = @_; my $chart = Chart::Clicker->new; my @x_copy = @$x; my @y_copy = @$y; my $series = Chart::Clicker::Data::Series->new( keys => $x, # if I use \@x_copy and values => $y, # \@y_copy here instead, there is no probl +em ); my $dataset = Chart::Clicker::Data::DataSet->new( series => [ $series ], ); $chart->add_to_datasets($dataset); $chart->write_output('test.png'); } my @x_axis; my %a_hash; for (my $t=0;$t<200;$t++){ $x_axis[$t] = $t*10; @{$a_hash{abc}}[$t] = int(rand(100)); } for (my $x=0;$x<10000;$x++) { &mysub(\@x_axis, \@{$a_hash{abc}}); print "."; }
As you can see in the comments if I use copied arrays there is no out of memory problem. If I return during the function anywhere before the $chart->write_output, there is no memory problem. But once $chart->write_output has been called, if I am using the original arrays (not local copies), I have an out of memory exception after few hundred graphs are created (in the above code it takes about 4000 graphs to trip the exception on a 2GB laptop, the original code is more complicated with multilined graphs etc and takes less iterations to die)

Is making a local copy of variables the only way to do this type of thing? Or am I messing up by passing the arrays in the way I am doing it? Or is chart::clicker at fault?

Many thanks in advance for your prayers and monastic guidance.

Update: It seems that although if I copy the arrays as described less memory is leaked, some is still leaked. If I copy the arrays as described the dataset I have (today) is able to complete (and not die) on the machine I am using but the process still grows with every call to the graph drawing sub. If I don't copy the arrays its not even able to complete. Since I will have to process larger datasets than the one I am testing with I don't even seem to have a workaround (apart from writing out temp files or some other kludge).

Replies are listed 'Best First'.
Re: Out of memory using chart::clicker
by Laurent_R (Canon) on Feb 20, 2014 at 22:30 UTC
    Hmm, I don't know anything about for Chart::Clicker, but it seems that you have a memory leak. When you do this:
    for (my $x=0;$x<10000;$x++) { &mysub(\@x_axis, \@{$a_hash{abc}}); print "."; }
    mysub will create 10000 objects, which should not be a problem so long as the objects have only a few hundreds of numerical data. But if your module stores also the generated graph, then you might be creating objects that are 100 kB large (or more). And then, you might run into a problem if the created objects are persistent and not released between two calls. I would suggest that you try to let each dataset fall out of scope between two calls. This way, presumably, Perl will release the memory each time and you should not have any trouble.

    Passing lexical copies of the arrays, rather than the references, might be the way to go.

      Thanks, I think you hit the nub of my confusion here. Since mysub is a sub and I am calling it, when it returns I thought everything it created itself (like I assume a large bitmap of the graph in an array for instance) got released? I guess thats dependent on everything inside that sub being lexical. But if chart::clicker complies with use strict, surely it would not compile if it contained non lexical variables (unless there is a no strict in chart clicker somewhere but I can't find it if there is).

      So I added use strict to the test script and realised I didn't define $x and $y inside the sub as lexical, fixed that and rerun the test and got the same result (out of mem).

      I don't want to copy each array if possible as theres a lot of data to be copied (actually now I realise copying won't really help). I can see that there is a different way to code this by not originally using a large multidimensional hash to sort my data in the first place, but using hashes makes the coding much easier (at least with my present mindset - maybe if I had originally thought of the problem differently it would be fine) - but, surely it should be possible to use hashes in this way?

      Re letting the dataset fall out of scope, is there a way of doing this without copying the array each iteration? I don't need the array once the sub has finished with it. I tried undefing after the call to mysub (in the original code each array ref passed into mysub was a different array hash element IE @{$hash{x}{y{z}}) so I used
      $mysub(\@{$hash{x}{y{z}}); undef @{$hash{x}{y{z}};
      but that didn't seem to work but maybe my method was incorrect.

      I guess memory leak questions are common but are there any obvious things I am doing wrong here? Is this a leak in the chart module resulting in simply not being able to use it so many times in the same execution?

        Yes, you are most probably right, since you are calling Chart::Clicker in a sub, what you do there should have lexical scope and allocated memory should be redeemed when you exit the sub as the references to objects fall out of scope. So, I somewhat overlooked part of what you are doing in your program and,as far as I can say, you do not seem to be doing anything wrong. It would appear, then, that the memory leak (if any, but it definitely looks so) probably occurs in the module you are calling or possibly in another module called by it.

        The solution (or rather workaround), that I used a couple of times in the past in vaguely similar situations, might be to have your Perl program process, say, a couple of hundred of datasets and then die (using the exec function or maybe forking before dieing or possibly doing it in a shell loop or some other similar means, I just remember I tried various possibilities and I don't remember exactly which one worked, and I can't check the final program before returning to work on Monday) after having called itself with parameters for the next group of datasets, and so on until your are done. This approach is far from being pretty, but, it seems to work.

        Update :

        Actually, I had not yet seen it when I just wrote the above, but davido said it before me and I can only agree with him: yes, forking and suiciding immediately after seems to be a good solution temporary fix.

Re: Out of memory using chart::clicker
by davido (Cardinal) on Feb 21, 2014 at 18:01 UTC

    I think that the long answer and the short answer are both that Chart::Clicker is leaking memory. Maybe not leaking in its own code, but somewhere between the primary module and its dependencies, there's a leak. I don't see in your code anything that you are doing seriously wrong. When you pass to an object's constructor a reference to an array, and later let that object fall out of scope, whether or not the array is still in scope should never affect the object's ability to be destroyed, freeing up its memory. My first suspicion was that the ref to the array was somehow being tied inside the object to a class that created a circular reference. But I don't see any use of tie; just a lot of Moose. However, the dependency chain is fairly heavy, and I didn't work my way up the chain in every direction in my effort to track down the culprit.

    If it were me, I would try to boil it down to an even smaller test case that demonstrates the leak, and submit it as a bug report via Chart::Clicker's RT. The author knows his code base and dependencies better than you. Certainly authors appreciate actionable reports -- ones where you've done the research and created a patch for them. And if you have the ambition to do that, I'm sure it would be welcomed. But at minimum, at least submit a report demonstrating the bad behavior.

    As for a temporary fix: The time honored solution is to fork children, work work work, then let them report back, and ultimately each one exits. By instantiating your Chart::Clicker objects inside of short-lived child processes, you never get into a situation where a memory leak in Chart::Clicker has a long enough life to become a problem. It's an ugly truth, but it works.


    Dave

      I have previously run into the same memory leak issue issue with Chart::Clicker. As you noted, it has a fairly heavy dependency chain. I didn't get very far trying to follow it. This was for a project at $WORK and I need to get things done.

      My work around was exactly as you said: fork off children to do the work. This was just a one off for exploration, so I didn't spend much time trying to optimize it. I forked a child for each chart, and kept the total child count under $n for some appropriate value of $n.

        Just a small example of memory leak
        #!/usr/bin/perl use strict; use Chart::Clicker; use Chart::Clicker::Context; use Chart::Clicker::Data::Range; use Chart::Clicker::Renderer::Bubble; use Chart::Clicker::Data::DataSet; use Chart::Clicker::Data::Series::Size; print "run 'top' in linux and look in perl process!\n"; my $j = 100000000; for (my $i =1;$i < $j;$i+=1){ print "$i iteration\n"; mysub($i); } sub mysub{ my ($var_i) = @_; my @k = (1,2,3); my $cc = Chart::Clicker->new(width => 800, height => 400 , format +=> 'png', padding => 10); my $series = Chart::Clicker::Data::Series::Size->new( keys => \@k, values => \@k, sizes => \@k, name => "$var_i", ); my $ds = Chart::Clicker::Data::DataSet->new( series => [ $series ] ); $cc->add_to_datasets($ds); my $defctx = $cc->get_context('default'); $defctx->renderer(Chart::Clicker::Renderer::Bubble->new); $cc->write_output($var_i); #undef $cc; print "\tmemory leak after 'cc->write_output'\n"; }
        I can't find any solution and need quick help. I don't want to use any other perl chart lib. But I think I have no choice. :( With pleasure BorisPlus
Re: Out of memory using chart::clicker
by zentara (Cardinal) on Feb 21, 2014 at 17:46 UTC
    Just from glancing at your code, and knowing what memory gluttens packages are, I can see one giant problem. You have a loop which makes 10000 iterations, and in each cycle, you create a new $chart object and it's associated helper objects.

    For some reason, I believe your sub mysub{} closure isn't cleaning up all those objects, as you would expect.

    In my experience with other graphic modules, this can be avoided by creating only 1 set of global objects, and as you go thru your loops, you clean out and reuse the global object. This prevents the memory problem ... reuse your objects. See perldoc -q clear for how to clear a package. MJD's example code cleans out a package for reuse.


    I'm not really a human, but I play one on earth.
    Old Perl Programmer Haiku ................... flash japh
      I've got to say, that sounds like a really good thing to try which I never thought of myself. If I am writing something heavy from scratch myself I would use this kind of technique (I always try to think of reducing allocation overhead etc even though I don't know if it really helps in perl but I always have c in the back of my mind) but it just didn't occur to try it this time as I was thinking along the lines of 'must start with new object each time to make sure its nice and clean'. I guess its dependent on the module code working properly though otherwise things like reused arrays of a smaller size second time round might still contain some old data? Regardless it will be an interesting test to run. Thanks
Re: Out of memory using chart::clicker
by djzort (Sexton) on Mar 20, 2014 at 04:05 UTC

    It seems that Chart::Clicker is creating circular references which perl can't unpick.

    Using Chart::Clicker via Catalyst, and +CatalystX::LeakChecker

    [debug] Circular reference detected: .--------------------------------------------------------------------- +---. | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[0]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[0]->{component_list}->{compone +n- | | ts}->[0]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[0]->{component_list}->{compone +n- | | ts}->[1]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[0]->{component_list}->{compone +n- | | ts}->[2]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[0]->{component_list}->{compone +n- | | ts}->[3]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[0]->{component_list}->{compone +n- | | ts}->[4]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[0]->{component_list}->{compone +n- | | ts}->[5]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[0]->{component_list}->{compone +n- | | ts}->[6]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[0]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[1]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[2]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[3]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[4]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[5]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[6]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[7]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[8]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[9]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[1]->{component_list}->{compone +n- | | ts}->[10]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[2]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[2]->{component_list}->{compone +n- | | ts}->[0]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[2]->{component_list}->{compone +n- | | ts}->[1]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{component_list}->{components}->[2]->{component_list}->{compone +n- | | ts}->[2]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{grid}->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{render_area}->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{render_area}->{component_list}->{components}->[0]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{render_area}->{component_list}->{components}->[1]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [0]->{render_area}->{component_list}->{components}->[2]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [1]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [1]->{component_list}->{components}->[0]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [1]->{component_list}->{components}->[1]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [1]->{component_list}->{components}->[2]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [1]->{component_list}->{components}->[3]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{component_list}->{components}- +>- | | [2]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{domain_ +a- | | xis}->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{domain_ +a- | | xis}->{component_list}->{components}->[0]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{domain_ +a- | | xis}->{component_list}->{components}->[1]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{domain_ +a- | | xis}->{component_list}->{components}->[2]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{domain_ +a- | | xis}->{component_list}->{components}->[3]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{domain_ +a- | | xis}->{component_list}->{components}->[4]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{domain_ +a- | | xis}->{component_list}->{components}->[5]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{domain_ +a- | | xis}->{component_list}->{components}->[6]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[0]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[1]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[2]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[3]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[4]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[5]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[6]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[7]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[8]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[9]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{range_a +x- | | is}->{component_list}->{components}->[10]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{contexts}->{default}->{rendere +r- | | }->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{legend}->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{legend}->{component_list}->{co +m- | | ponents}->[0]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{legend}->{component_list}->{co +m- | | ponents}->[1]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{legend}->{component_list}->{co +m- | | ponents}->[2]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{legend}->{component_list}->{co +m- | | ponents}->[3]->{layout}->{component} + | | $ctx->{stash}->{graphics_primitive}->{marker_overlay}->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[0]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[0]->{component_list}->{components}->[0]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[0]->{component_list}->{components}->[1]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[0]->{component_list}->{components}->[2]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[0]->{component_list}->{components}->[3]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[0]->{component_list}->{components}->[4]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[0]->{component_list}->{components}->[5]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[0]->{component_list}->{components}->[6]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[0]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[1]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[2]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[3]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[4]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[5]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[6]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[7]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[8]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[9]->{layout}->{compone +n- | | t} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[1]->{component_list}->{components}->[10]->{layout}->{compon +e- | | nt} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[2]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[2]->{component_list}->{components}->[0]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[2]->{component_list}->{components}->[1]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{component_list}->{comp +o- | | nents}->[2]->{component_list}->{components}->[2]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{grid}->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{render_area}->{clicker +} | | $ctx->{stash}->{graphics_primitive}->{plot}->{render_area}->{compone +n- | | t_list}->{components}->[0]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{render_area}->{compone +n- | | t_list}->{components}->[1]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{plot}->{render_area}->{compone +n- | | t_list}->{components}->[2]->{clicker} + | | $ctx->{stash}->{graphics_primitive}->{title}->{layout}->{component} + | '--------------------------------------------------------------------- +---'

      after some fun with Data::Dumper, i have come up with the following. YMMV.

      # hack away at circular references! $chart->{component_list}->{components} = undef; $chart->{'contexts'}{'default'}{'range_axis'}{'parent'}{'component +_list'}{'components'} = undef; $chart->{legend}->{component_list}->{components} = undef; $chart->{plot} = undef; $chart->{contexts}->{default} = undef; $chart->{legend}->{clicker} = undef; $chart->{marker_overlay}->{clicker} = undef; $chart->{title}->{layout}->{component} = undef;
        I've released new versions of Graphics::Primitive (0.65) and Chart::Clicker (2.88) that seek to address this issue. If you still see leaks I'd appreciate another dump form the leakchecker. That was quite helpful.