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

I'm working on a Tk project that includes the ability to edit a set of filters, much like email filters. I've come up with a scheme that uses two HList widgets to manage this.

The user interaction works nicely, but I've discovered that I've got some memory issues. Every time I select a new rule for editing, I gobble up about 110 K of ram.

Before I go into much more detail, my questions are:

Here's a gross little ASCII picture of the interface:

+--------------------------------+ | [ ] A Rule X | | [X] A Different Rule X | | + | +--------------------------------+ Name _____________________ Run if [ANY =] conditions: +--------------------------------+ |[FIELD1 =] [ > =] __________ X | |[FIELD2 =] [ < =] __________ X | |[FIELD3 =] [ is =] [ value =] X | +--------------------------------+ Legend: [FOO =] is meant to represent an Optionmenu The X and + are buttons for adding or deleting items _____ are Entry widgets The borders denote my HList widgets.

How it works

When a user selects a rule in the top HList, it is displayed in the bottom HList. The I copy the rule into my "working copy" aka my object's rule attribute. The Rule and Clause objects are basic Class::Struct based objects. Then I clean out the bottom HList by explicitly destroying the widgets in each item, and delete each entry. Then I create new entries and items and populate them with new widgets. A lot of widgets are created and destroyed in this process, but the only other option I can see is creating a pool of widgets and parceling them out or creating new ones as needed--a task I'd rather not manage.

sub EditRule { my $self = shift; my $rule = shift || 'NO_COPY'; if ( $rule ne 'NO_COPY') { $rule->copy($self->original_rule); $rule->copy($self->rule); } # This is my HList widget my $clause_list = $self->clause_list; # Here we remove all items from the HList. foreach my $path ( $clause_list->info('children') ) { print "Deleting $path\n"; foreach (0..3) { next unless $clause_list->item('exists', $path, $_); my $w = $clause_list->item('cget', $path, $_, '-widget'); # This is testing code where I try explicitly clearing out + bits the widgets I use. # It could be refined by writing an arg list to an array a +nd passing it to a generic $w->configure call. if ( ref $w eq 'Tk::Button' ) { print "\tCleaning button\n"; $w->configure( '-command'=> undef, -textvariable => undef ); } elsif ( ref $w eq 'Tk::Optionmenu' ) { print "\tCleaning Optionmenu\n"; $w->options($NULL_OPTS); $w->configure( '-command'=> undef, -textvariable => undef, ); } elsif ( ref $w eq 'Tk::Entry' ) { print "\tCleaning Entry\n"; $w->configure( -textvariable => undef, ); } $w->DESTROY; } $clause_list->delete('entry', $path); } # This gets a list of conditions or clauses for the rule # Clause is another Class::Struct based object. my $clauses = $self->rule->clauses; # Here I make a new HList item to house the NEW CLAUSE button. # methods called _W_blah_blah return a widget. $clause_list->add('new_clause'); $clause_list->itemCreate('new_clause',0, -itemtype => 'window', -widget => $self->_W_new_clause(), ); # Here I add a new entry for each clause in the rule. foreach my $i ( 0..$#{$clauses} ) { $self->DisplayClause($i); } $self->join_op->configure( -state => 'normal', ); } # END EditRule ----------------------------------------- sub DisplayClause { my $self = shift; my $i = shift; # Clause index my $clause_list = $self->clause_list; # Get my HList widget my $clause = $self->rule->clauses->[$i]; # Get the clause to displ +ay. # I know that no item exists at HList path $i. $clause_list->add($i, -before=>'new_clause'); # Create the Tix display items. # _W_foobar returns a widget. # different field values have different operator and value widgets +. $clause_list->itemCreate($i, 0, -itemtype => 'window', -widget => $self->_W_select_field($clause, $i), ); $clause_list->itemCreate($i, 1, -itemtype => 'window', -widget => $self->_W_select_op($clause), ); $clause_list->itemCreate($i, 2, -itemtype => 'window', -widget => $self->_W_select_value($clause), ); $clause_list->itemCreate($i,3, -itemtype => 'window', -widget => $self->_W_delete_clause($i), ); }

Any ideas? Comments? Rebukes for my foolishness?


TGI says moo

Replies are listed 'Best First'.
Re: Tk, HLists and memory leakage
by zentara (Cardinal) on Oct 20, 2005 at 10:16 UTC
    Is there an reasonable way to see what data aren't being garbage collected?

    What I do is place Data::Dumper statements at critical points in the program, and watch for the variables growing....like extra hash keys.

    If I really need to rethink my wanton creation and destruction of widgets, can anyone suggest some strategies for keeping a pool of widgets around and configuring them as needed?

    I think you answered your own question. Hlist entries can be reconfigured, so don't delete and create new ones.

    You usually do this, by separating your actual data storage, from the displaying of the data. In other words, don't use the widget to store your data, use the widget to display data stored in another hash. That way the widgets are just a "window to the screen" to display what is in some underlying data hash. They remain the same, just being reconfigured to display the new data.

    Here are a few examples.


    I'm not really a human, but I play one on earth. flash japh

      Your suggestion of separating the data storage from the widget is what I am doing already--unless you are suggesting that instead of pointing my widgets at values in an object instance that is dedicated to the widgets, I should be creating a separate hash. I don't see how that could help. My description of the issues must have been unclear.

      I don't see the leakage when I use items of type text. The problem comes up with items of type 'window' or 'widget'. Memory seems to be reclaimed when I create and destroy text items.

      As to using Data::Dumper, which values do I dump? I have already explicity destroyed my widgets, so I can't dump them. Dumping the MainWindow shows me nothing useful, same with the HList widget. What should I be dumping? What I'd really like to see is a memory map with each stored value and a set of reference counts for each.

      Keeping items around and then configuring is fine if you have a static, well defined number of items you want to display. In my case, a rule may have one or 100 clauses. Creating and managing a pool of identical HList entries seems to be a hassle. Add to this the fact that I need to be able to change which widget is displayed in a given item, based on the values selected in other widgets (eg Field_1 has a list of legal values, but Field_2 can be any integer, so I use a menu and an entry, respectively, to handle input). Should I then pre-create entries with each possible set of widgets to meet my needs?

      I could reduce the size of a precaching implementation by allocating a bank of each type of widget: HList items, Optionmenus, Entries, and Checkbuttons. Then I would need to track which ones were active, and which ones weren't, and probably handle making more as needed. Then available widgets could be assinged to available items in available entries. If I have to do all that, what benefit does the HList really offer over rolling my own similar functionality using a Frame and grid()? I chose the HList widget because it looked like should handle many of these issues for me. I am beginning to this this was a mistake.


      TGI says moo

        First, it will always be unclear to me what you are doing, because I'm not writing your app, and my head just isn't into what you are doing. All I can do is relate my similar experiences with HLists and mem leaks.

        As far as what widgets or hashes to dump, it is kind of an "art" to figure out where and what to dump. Try linux memory leak monitor and carefully watch what happens on each click of your app. You should be able to deduce which subs are being called when you see memory jumps, then dump all hashes involved. You can also use Devel::Size to get an idea of the size of the variables.

        As far as dealing with 1 or 100 clauses, and keeping a static number of widgets; you can do it. Just keep track of each widget-set you create for each clause, and store it all in a hash. If you don't need it, don't destroy it, just unpack it, and it will be there for future re-use. I know it sounds like a hassle to track and store all created widgets, unpacking, reconfiguring and repacking them as needed, but that is what you will have to do if you want to manage your memory and keep it from the constant upward increase.

        And of course, like you said, maybe the HList isn't the right widget for you, if your entries don't all use the same widgets. Maybe you could devise a few different Hlist's, each configured to display a different set of widgets, and call that HList model depending on your display needs. Maybe Tk::TableMatrix would better suit your needs?


        I'm not really a human, but I play one on earth. flash japh