Document Information

Last modified:
2008/08/25 09:20 by thron7

This is documentation for qooxdoo 0.7.x only. You might be interested in consulting the latest docs.

Destructor support

Basics

To destruct existing objects at the end of your application is an important feature in the ever growing area of web applications. Widgets and models are normally handling a few storage fields on each instance. These fields need the dispose process to work without memory leaks.

Normally, JavaScript automatically cleans up. There is a built-in garbage collector in all engines. But these engines are more or less buggy. One problematic issue is that browsers differentiate between DOM and JavaScript and use different garbage collection systems for each (This does not affect all browsers, though). Problems arise when objects create links between the two systems. Another issue are circular references which could not be easily resolved, especially by engines which rely on a reference counter.

To help the buggy engines to collect the memory correctly it is helpful to dereference complex objects from each other, e.g. instances from maps, arrays and other instances. You don’t need to delete primitive types like strings, booleans and numbers.

qooxdoo has solved this issue from the beginning using the included “dispose” methods which could be overridden and extended by each class. However through the rapid growth of qooxdoo and the classes it got more and more verbose and this way also a bit confusing to manage the dispose handling.

qooxdoo 0.7 introduces a new class declaration. This class declaration supports real “destructors” as known from other languages. These destructors are part of the class declaration. The new style makes it easier to write custom destructor/disposer methods because there are many new helper methods and the whole process has been streamlined to a great extend.

Compare for yourself

qooxdoo 0.6

qx.Proto.dispose = function()
{
  if (this.getDisposed()) {
    return;
  }
 
  this._data = null;
  this._moreData = null;
 
  if (this._buttonOk)
  {
    this._buttonOk.dispose();
    this._buttonOk= null;
  }
 
  if (this._buttonCancel)
  {
    this._buttonCancel.dispose();
    this._buttonCancel= null;
  }
 
  if (this._childs)
  {
    for (var i=0; i<this._childs.length; i++)
    {
      this._childs[i].dispose();
      this._childs[i] = null;
    }
 
    this._childs = null;
  }
 
  my.superclass.prototype.dispose.call(this);
}

qooxdoo 0.7

destruct : function()
{
  this._disposeFields("_data", "_moreData");
  this._disposeObjects("_buttonOk", "_buttonCancel");
  this._disposeObjectDeep("_childs", 1);
}

The result

The resulting code is much more appealing and hopefully leads to more classes with correct destruct handling than in the past.

At a glance

  • An “is this instance already disposed” check is not needed anymore
  • Also no need to call the superclass
  • Finally in most cases the existing helper methods _disposeFields, _disposeObjects and _disposeObjectDeep can replace existing, more complex statements.

What the functions do

  • _disposeFields: Deleting each key name given from the instance. This is the fastest of the three methods. It basically does the same as the nullify used in qooxdoo 0.6.
  • _disposeObjects: Dispose the objects (qooxdoo objects) under each key and finally delete the key from the instance like _disposeFields.
  • _disposeObjectDeep: This methods just works for one key. It does not iterate through the arguments, like the other two. The second parameter defines the depth to follow inside the object structures. To just delete the object and each reference inside the object define a second parameter of 0. With a value of 1 each object inside gets disposed, too, not just dereferenced. This also works on deeper structures, e.g. on an array of maps which contain references to widgets).

How to test the destructor

Enable the debug code

The destructor code of 0.7 allows you an in-depth analysis of the destructors and finds fields which may leak etc. The DOM tree gets also queried for back-references to qooxdoo instances. These checks are not enabled by default because of the time they need on each unload of a typical qooxdoo based application.

To enable these checks you need to select a variant and configure a setting.

The variant qx.debug must be on. The setting qx.disposerDebugLevel must be at least at 1. Higher values mean more output. For a general analysis 1 should be enough. You need to pass the following flags to the generator:

--use-variant qx.debug:on --use-setting qx.disposerDebugLevel:1

If you are a user of the Makefile based skeleton system you can easily configure these values by adding the above options to the build and/or source options like shown below:

APPLICATION_ADDITIONAL_SOURCE_OPTIONS = --use-variant qx.debug:on --use-setting qx.disposerDebugLevel:1
APPLICATION_ADDITIONAL_BUILD_OPTIONS = --use-variant qx.debug:on --use-setting qx.disposerDebugLevel:1

If you are not only using the qooxdoo framework, but work with the framework source itself, you may alternatively change the setting qx.disposerDebugLevel in the source file of class qx.core.Object. For regular application development. though, modifying the local framework code is not recommended.

Log output from these settings should look something like this:

35443 DEBUG: testgui.Report[1004]: Disposing: [object testgui.Report]FireBug.js (line 75)
Missing destruct definition for '_scroller' in qx.ui.table.pane.FocusIndicator[1111]: [object qx.ui.table.pane.Scroller]Log.js (line 557)
Missing destruct definition for '_lastMouseDownCell' in qx.ui.table.pane.Scroller[1083]: [object Object]Log.js (line 557)
036394 DEBUG: testgui.Form[3306]: Disposing: [object testgui.Form]FireBug.js (line 75)
Missing destruct definition for '_dateFormat' in qx.ui.component.DateChooserButton[3579]: [object qx.util.format.DateFormat]Log.js (line 557)
Missing destruct definition for '_dateFormat' in qx.ui.component.DateChooserButton[3666]: [object qx.util.format.DateFormat]Log.js (line 557)

The nice thing here is that the log messages already indicate which dispose method to use: Every “Missing destruct...” line contains a hint to the type of member that is not being disposed properly, in the “[object ...]” part of the line. As a rule of thumb

  • for native Javascript types (Number, String, Object, ...) use _disposeFields
  • for qooxdoo objects (e.g. qx.util.format.DateFormat, testgui.Report, ...) use _disposeObjects or _disposeObjectDeep

Using a harness to check for leaks

Another way of testing destruction is to construct and destruct a widget a number of times and observe objects which are leaked. For example, the harness shown below will create 5 instances of widgets.widget1, waits 20 seconds and then destructs them again. Object counts are shown before and after the test.

(The timer is useful if your widget does asynchronous fetches of data – if it doesn’t then you won’t need to be quite so sophisticated)

var container1 = new qx.ui.layout.VerticalBoxLayout;
container1.addToDocument ();
 
alert(qx.dev.ObjectSummary.getInfo());
 
var container2 = new qx.ui.layout.VerticalBoxLayout;
container1.add (container2);
 
for (var i = 0 ; i < 5 ; i++)
{
    var widget =  new widgets.widget1 ();
    container2.add (widget);
}
 
qx.client.Timer.once (function ()
{
    container1.remove (container2);
    container2.dispose ();
 
    alert(qx.dev.ObjectSummary.getInfo());
}, this, 20000);

When you’ve seen how many Objects have leaked, try to put into perspective how many will build up over the life of a running application. So, for instance, if you have a simple application which creates all the widgets up front, runs for a while and then lets the user quit, then the cleanup isn’t too important.

If you have an application which remains open for a few hours and creates and destructs a screen of widgets every 10 minutes or so, then if you leak a few dozen objects each time, then the user’s PC may start to creak as the day goes on.

It’s also possible to quickly consume a PC’s memory by creating big trees or tables which you don’t clean up properly, or fast moving animations where you leak a few objects every update.

Being able to run the ObjectSummary shown above from a running application periodically whill allow you to guage if you have a problem on your hands.

As a slight digression, if all the widgets that you create are attached as children to other widgets, disposing a parent widget will clean its children. Certain widget types do not sit as parent/children so you may need to take extra care with them. Some examples are:

  • rpc widgets
  • managers
  • tooltips
  • (other contributions gladly accepted)

There are a few ways to deal with these, but one way that works well is to accumulate them in a private array, and then in the destructor dispose the array with _disposeObjectDeep.

Disposing an application

You can dispose any qooxdoo based application by simply calling qx.core.Object.dispose(). The simplest possibility is to use the command line included in Firebug. Another possibility is to add a HTML link or a button to your application which executes this command.

Information

Last modified:
2008/08/25 09:20 by thron7

Account

Not logged in

 
 

Rich Ajax Platform (RAP)

RAP uses qooxdoo, Java and the Eclipse development model to build rich web applications. Read more...

qooxdoo Web Toolkit (QWT)

Similar to GWT this framework allows to create impressive qooxdoo applications just using Java. Read more...

Pustefix

Pustefix is a MVC-based web application framework using Java and XML/XSLT. Read more...

 
SourceForge.net Logo

Bad Behavior has blocked 0 potential spam attempts in the last 7 days.