G'day nzsvz9,
This was something that interested me because I write a lot of Tk programs for personal use, and many of them involve images. I've never run out of memory — possibly because I've got lots to start with; 32GB on my current machine — so it has never occurred to me to check for memory leaks.
Your idea of passing around Tk-related data in a hash is, at least in my opinion, a good one. I do it myself and, in the code below, I've provided some basic examples of my type of usage. I do not, however, use package variables; furthermore, my lexical $TK is hidden from almost all of the code in an anonymous block.
You also need to manage your %TK. You didn't show any working code nor give any idea of how many images you're dealing with or how big they are; however, I suspect this is, at least part of, your memory problem. There may well be all sorts of other data you're carrying around well past its use-by-date.
The following code I ran twice. First without, then with, these two lines of code (they're almost at the end of the script):
(delete $TK->{img}{$name})->delete(); $TK->{gui}{can}->delete('all');
The first run chewed up about 200MB which was immediately reclaimed when I used the "Exit" button. The second run used no appreciable amount of memory; system memory was unchanged after hitting "Exit".
I can't fix your code because you didn't show it. I attempted to keep most of what you outlined in your description. Hopefully, you can get some ideas from what follows to make improvements.
#!/usr/bin/env perl use strict; use warnings; use Tk; { my $TK = {}; $TK->{gui}{mw} = MainWindow::->new(); _build_gui($TK); my ($iter, $id) = (0); $id = $TK->{gui}{mw}->repeat(500, sub { $TK->{gui}{start}->invoke(); $TK->{gui}{mw}->after(200, sub { (shift @{$TK->{gui}{quits}})->invoke(); }); $id->cancel if $iter++ > 1_000; }); } MainLoop; sub _build_gui { my ($TK) = @_; $TK->{gui}{mw}->configure(-title => 'Test Tk::Photo Memory Leak'); $TK->{gui}{mw}->geometry('384x216+100+150'); _build_controls($TK); return; } sub _build_controls { my ($TK) = @_; $TK->{gui}{frame} = $TK->{gui}{mw}->Frame()->pack(); $TK->{gui}{start} = $TK->{gui}{frame}->Button( -text => 'Start', -command => sub { _popup($TK) } )->pack(); $TK->{gui}{frame}->Button( -text => 'Exit', -command => sub { exit } )->pack(); return; } sub _popup { my ($TK) = @_; $TK->{img}{id} = 0 unless exists $TK->{img}{id}; my ($w, $h) = (400, 300); $TK->{gui}{top} = $TK->{gui}{frame}->Toplevel(); $TK->{gui}{top}->geometry("${w}x$h+500+550"); $TK->{gui}{top}->overrideredirect(1); push @{$TK->{gui}{quits}}, $TK->{gui}{top}->Button(-text => 'Quit' )->pack(-side => 'bottom'); $TK->{gui}{can} = $TK->{gui}{top}->Canvas( )->pack(-anchor => 'center', -fill => 'both', -expand => 1); my $name = 'earth' . $TK->{img}{id}++; $TK->{img}{$name} = $TK->{gui}{top}->Photo( $name, -file => Tk->findINC('demos/images/earth.gif') ); $TK->{gui}{can}->createImage( $w/2, $h/2, -image => $name, -anchor => 'center' ); $TK->{gui}{quits}[-1]->configure(-command => sub { (delete $TK->{img}{$name})->delete(); $TK->{gui}{can}->delete('all'); $TK->{gui}{top}->destroy(); }); return; }
You should be able to run that without needing to change anything (except maybe the shebang line). The code itself will run on v5.8 (probably even older versions). The image, earth.gif, comes with Tk (it's used in the Widget Demo) so you should already have that.
— Ken
In reply to Re: Losing my memory
by kcott
in thread Losing my memory
by nzsvz9
| For: | Use: | ||
| & | & | ||
| < | < | ||
| > | > | ||
| [ | [ | ||
| ] | ] |