in reply to Out of memory using chart::clicker

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

Replies are listed 'Best First'.
Re^2: Out of memory using chart::clicker
by sn1987a (Curate) on Feb 21, 2014 at 19:18 UTC

    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
        Hi monks and thanks for all the replies. I continued to work on this over the last couple of days and found that even if everything is declared as my variables inside the sub with nothing global and nothing passed in, the leak still happens, so although I don't feel confident enough to say on my own that the leak is in the module, I think from the replies above that the leak must be in chart::clicker. Heres what I tried and this still leaks:
        #!/usr/bin/perl -s -w use strict; use Chart::Clicker; use Chart::Clicker::Data::Series; use Chart::Clicker::Data::DataSet; $| = 1; sub mysub { my @x_axis; my @y_axis; for (my $t=0;$t<200;$t++){ $x_axis[$t] = $t*10; $y_axis[$t] = int(rand(100)); } my $chart = Chart::Clicker->new; my $series = Chart::Clicker::Data::Series->new( keys => \@x_axis, values => \@y_axis, ); my $dataset = Chart::Clicker::Data::DataSet->new( series => [ $series ], ); $chart->add_to_datasets($dataset); $chart->write_output('test.png'); } for (my $x=0;$x<10000;$x++) { &mysub; print "."; }
        So I will both report it to the module writer and try to find it next week myself (but its the first time I have delved into such a complicated module so I think it might be quite difficult and I'm not sure how well I will get on). Looking at the dependencies it looks like there are components written in other languages than perl which although I understand some of, they can tend to be very large (ie C) and I don't have any debugging tools set up for that kind of thing (or much experience with memory leaks etc).

        Anyway I thought about the problem a bit more and rewrote the program to write out files which contain one graphs worth of data with a header to describe the information required as state (like absolute time etc). This works well but obviously is less elegant, however its probably more in the spirit of perl as it Just Gets The Job Done nicely in the real world. Although I haven't added the code to fork / exec yet, I will perhaps fork a few child processes to do the graph writing (dependent on the number of processors would be nice) to take advantage of dual or quad cores without the complexity and room for errors that threaded code has. These forked processes could then reexec after a particular number of iterations to deal with the memory leaks.

        So all in all I think thats a workable solution but it would be nice to find the leaks in the module still. I wonder if the fact that the process is growing so large would slow it down at all? IE does constantly asking for more memory from the OS slow it down much? (and should that affect my decision as to how many charts to let each child process produce before they commit hari kari?)

        Lastly a new question has arisen from this but I think its so unrelated that I better post it as a new question but for those who see it, the question relates to this same program (related to how to input a very large amount of data from an external program called from my program). Thanks very much to all who commented.
        Hi Boris, Just in case you didn't see the above, consensus seems to be that the module or its dependancies have a leak which might be tricky to find for someone in a hurry like it sounds you are, so a workaround is probably the best route. Either fork children to process the graphs (but be careful you don't start too many children at once if you do this), or the way round I have used currently is to write temp files of data and then subsequently in the same program I start to process the graphs and write them out. Once I have processed n graphs (the value of n depending on how large you are happy for your perl process to grow) you can exec your perl process (perhaps with a flag to indicate that it can progress directly to the graph writing section rather than creating all the data files again) and continue to process another n data files until your list of data files is all converted to graphs when your perl process can exit.