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

Dear brothers in perl,

I have a question. I wrote a script which creates old-school html-marque like window with scrolling text. Everything works good, except... huge memory leaks. The amount of leaks depends of length of string, which we pass for displaying. What I've found in Gnome System Monitor is that Heap grows extremely large. It must be some obvious bug in code. I can't find it.

Please help. Usage: ./script_filename <Name of file with string to be displayed>
#!/usr/bin/perl -w use strict; use constant WIDTH => 1000; use constant HEIGHT => 100; use constant SPEED => 15; use Cairo; use Glib qw(TRUE FALSE); use Gtk2 -init; use Gtk2::Pango; my ($text,$ticks); sub draw_text { my ($cr) = @_; my $layout = Gtk2::Pango::Cairo::create_layout($cr); $cr->push_group; $layout->set_text($text); my $desc = Gtk2::Pango::FontDescription -> from_string("Sans Bold +57"); $layout -> set_font_description($desc); $layout->set_alignment("PANGO_ALIGN_LEFT"); Gtk2::Pango::Cairo::update_layout($cr, $layout); my @size = $layout->get_pixel_size; $cr -> translate(-($ticks*SPEED)%$size[0],0); $cr->set_source_rgb(1.0,1.0,1.0); $cr->fill(); $cr -> set_source_rgb(0.0,0.0,0.0); my $count = int(WIDTH/$size[0]); $cr->translate(-$size[0]*2,0); for(my $i = 0; $i<=$count+1; $i++){ $cr->translate($size[0],0); Gtk2::Pango::Cairo::show_layout($cr,$layout); } $cr->pop_group_to_source; $cr->paint; undef $cr; undef $layout; } my $window = Gtk2::Window->new(); $window -> signal_connect(delete_event => sub { Gtk2 -> main_quit(); }); my $area = Gtk2::DrawingArea -> new(); sub draw { my $widget = $area; my $cr = Gtk2::Gdk::Cairo::Context -> create($widget->window()); $cr->rectangle(0,0,WIDTH,HEIGHT); draw_text($cr,$ticks); } sub timer { $ticks ++; draw(); return 1; } $text = 'Please specify your input file'; if($ARGV[0]){ open(INPUT_FILE, "$ARGV[0]"); $text = <INPUT_FILE>; } $ticks = 0; $window -> set_default_size(WIDTH,HEIGHT); $window -> add($area); $window->show_all(); my $timer = Glib::Timeout->add(1000/24,\&timer,'',); Gtk2->main();

Replies are listed 'Best First'.
Re: Bad, bad memory leaks in simple script
by bruceb3 (Pilgrim) on Sep 08, 2007 at 05:12 UTC
    Following from the previous post; Move the call to Gtk2::Gdk::Cairo::Context->create out of the draw sub. A new Cairo context is being created in the timer sub each time it's being called. Have one sub for the updating of the $ticks and one for the initial creation of the Context.
Re: Bad, bad memory leaks in simple script
by erroneousBollock (Curate) on Sep 08, 2007 at 04:04 UTC
    I can't see anything obvious you've done to cause a cycle or long-lived closure (though someone more familiar with the Gtk bindings might do).

    Personally, I don't like the closures over your two globals, but that's unlikely to be the source of your issues.

    As an experiment, try to move some of the font logic (and anything else that wouldn't need to be computed each time) out of draw_text.

    It may be that the Gtk2 (or Gtk2::Pango) module does some resource caching that you're not aware of. If so, try using Devel::Leak to find those leaks.

    -David

Re: Bad, bad memory leaks in simple script [solved]
by zentara (Cardinal) on Sep 08, 2007 at 14:38 UTC
    Your code still has alot of problems, like the unresponsiveness when trying to kill it. And the God-awful 98% cpu use for the xserver while running your script( probably caused by the constant redraws).The DrawingArea may not be the best choice of widget for doing this type of thing, although some really clever person may perfect it.

    The Gnome2::Canvas is probably the best widget choice for this, see Gtk2 Scrolling Text.

    Also check out GMeM -- a Gtk2 memory monitor utility for your future Gtk2 memory monitoring.


    I'm not really a human, but I play one on earth. Cogito ergo sum a bum
Re: Bad, bad memory leaks in simple script
by Solak (Novice) on Sep 08, 2007 at 13:42 UTC
    Thanks for your help. This is how i solved the problem:
    #!/usr/bin/perl -w use strict; use constant WIDTH => 1000; use constant HEIGHT => 100; use constant SPEED => 1; use Cairo; use Glib qw(TRUE FALSE); use Gtk2 -init; use Gtk2::Pango; my ($text,$ticks, $cr, $layout, $desc, $area); sub initialize { $cr = Gtk2::Gdk::Cairo::Context -> create($area->window()); $layout = Gtk2::Pango::Cairo::create_layout($cr); $desc = Gtk2::Pango::FontDescription -> from_string("Sans Bold 57" +); $layout -> set_font_description($desc); $layout->set_alignment("PANGO_ALIGN_LEFT"); } sub draw_text { $cr->push_group; $layout->set_text($text); Gtk2::Pango::Cairo::update_layout($cr, $layout); my @size = $layout->get_pixel_size; $cr -> translate(-($ticks*SPEED)%$size[0],0); $cr->set_source_rgb(1.0,1.0,1.0); $cr->fill(); $cr -> set_source_rgb(0.0,0.0,0.0); my $count = int(WIDTH/$size[0]); $cr->translate(-$size[0]*2,0); for(my $i = 0; $i<=$count+1; $i++){ $cr->translate($size[0],0); Gtk2::Pango::Cairo::show_layout($cr,$layout); } $cr->pop_group_to_source; $cr->paint; } my $window = Gtk2::Window->new(); $window -> signal_connect(delete_event => sub { Gtk2 -> main_quit(); }); $area = Gtk2::DrawingArea -> new(); sub draw { $cr->rectangle(0,0,WIDTH,HEIGHT); draw_text($cr,$ticks); } sub timer { $ticks ++; draw(); return 1; } $text = 'Please specify your input file'; if($ARGV[0]){ open(INPUT_FILE, "$ARGV[0]"); $text = <INPUT_FILE>; } $ticks = 0; $window -> set_default_size(WIDTH,HEIGHT); $window -> add($area); $window->show_all(); initialize; my $timer = Glib::Timeout->add(1000/60, \&timer ,'',); Gtk2->main();
    Was it Perl or GTK2 libraries bug with releasing objects after each timer callback? Should I avoid creating local (my) objects in subs?