Summary
SWT uses operating system resources to deliver its native graphics and widget functionality. Allocating and freeing operating system resources is traditionally an area of programming that is error prone. Languages that include garbage collection, such as the Java™ language, relieve the programmer from the burden of managing memory, but not from the allocation and freeing of operating system resources. This article discusses the simple strategy used by SWT to help application designers manage operating system resources.By Carolyn MacLeod and Steve Northover, OTI
November 27, 2001
When programming in a GUI operating system, you allocate operating system resources for widgets, images, fonts, and other graphical objects. Since there is a platform limit on the amount of resources you can allocate, you must be careful to free any objects that you allocate in your application. If you allocate a resource and do not free it when you are done with it, your application is "leaking" resources. An application that repeatedly leaks resources will eventually consume all of the available resources, forcing the user to reboot the operating system.
Fortunately, SWT makes resource allocation and disposal a straightforward process. There are only two rules that you need to remember when allocating and freeing SWT resource-based objects:
Rule 1: If you created it, you dispose it.
This is a simple rule.SWT makes it easy for you to remember when operating system resources are allocated: all SWT resource-based objects (like Color, Cursor, Display, Font, GC, Image, Printer, Region, Widget and subclasses) allocate any needed operating system resources in their constructor. There are no exceptions to this rule. There are no methods in SWT (other than constructors) that allocate operating system resources that the programmer must manage. If you didn't call the constructor, then you don't need to free the resources, so don't call dispose on the object. For example, in the following line of code, an operating system font is allocated:
Font font = new Font (display, "Courier", 10, SWT.NORMAL);
Since you called the Font constructor to create the resource, you must dispose the font when you are finished with it, as follows:
font.dispose();
In the following line of code, however, a constructor is not called:
Font font = control.getFont ();
Therefore, you must not call dispose. The font variable does contain an operating system font resource, but you did not allocate it. If you were to dispose of this font, you would be leaving the control without a font! The results are undefined. So, if you are using any getter that returns an SWT resource-based object that you did not allocate, do not dispose the object.
This rule occasionally leads to API that seems artificial. For example, GC.getClipping (Region) forces the programmer to create a Region in order to get the clipping region from a GC. Although it might have been a bit "prettier" to provide GC.getClipping () that returned a Region, this would break the rule because it would have to allocate an operating system resource outside of a constructor. While it could be documented that the programmer needs to free the Region returned by GC.getClipping (), programmers don't always read the documentation. Making the programmer call the constructor for the Region makes it clear that it is the programmer's responsibility to free the resource.
Rule 2: Disposing the parent disposes the children.
When you dispose a Shell, its children are disposed. In fact, disposing any Composite will dispose all of the Composite's children. Disposing a Menu disposes all menu items. Disposing a Tree or TreeItem disposes all child items.
Why does this make sense? Apart from the obvious burden of having to dispose each child if this were not the case, the fact that a widget cannot exist in the operating system without a parent implies that when the parent is disposed, the child must also be disposed.
What about the fonts and colors that you created and set into a Widget? Since they are not children of the widget, they are not disposed when the parent is disposed. A widget will never dispose a resource that you allocated. To do so would mean that the widget was breaking rule 1. Because fonts and colors can be shared by different widgets, disposing a resource in one widget would dispose it in another, leading to unpredictable results.
There are two extensions to rule 2. These are places where a relationship exists that is not strictly a parent-child relationship, but where it still makes sense for rule 2 to apply.
MenuItem.setMenu: Disposing a cascade MenuItem that has a submenu set with setMenu (Menu) disposes the submenu. This is a natural extension of rule 2. It would be a burden to the programmer to dispose each individual submenu. It's also common behavior in most operating systems to do this automatically. Both Windows® and X dispose submenus when a cascade menu item is disposed.
Control.setMenu: Disposing a Control that has a pop-up menu set with setMenu (Menu) disposes the pop-up menu. Many application programmers expected this behavior, even though many operating systems don't do this automatically. Leaving the application programmer responsible for disposing a pop-up menu when disposing the Control led to temporary leaks. (The leak was only temporary because the pop-up menu is eventually disposed when the shell is disposed).
Most attempts to use the Java garbage collector to manage operating system resources involve finalization. When an object is about to become garbage, the garbage collector sends it the finalize message. This seems like a logical place to free the resource, right?
Conventional wisdom says that managing operating system resources in finalization is difficult and error prone. To quote Joshua Bloch in his "Java Series" book Effective Java,
"Finalizers are unpredictable, often dangerous, and generally unnecessary. Their use can cause erratic behavior, poor performance, and portability problems." [JB, 2.6].
One of the main reasons that this statement is true is that there is no guarantee how soon after an object becomes garbage that the finalize method for the object will run. Even if you could predict this time interval for a particular VM, you certainly couldn't guarantee it for new versions of the VM. Worse still, if there is plenty of memory or if an exception occurs in an object's finalize method, there is no guarantee that the object will be finalized at all! All of this is stated right in The Java Language Specification:
"The Java programming language does not specify how soon a finalizer will be invoked, except to say that it will happen before the storage for the object is reused. ... If an uncaught exception is thrown during the finalization, the exception is ignored and finalization of that object terminates." [JLS, 12.6].
Releasing operating system resources is critical to correct program execution. Freeing resources at some unknown time in the future (or not at all) typically leads to program failure. Here are some examples of typical problems encountered when using finalization:
The non-deterministic nature of finalization is sufficient reason to avoid using it to free resources, but even if we decided to go against conventional wisdom and implement a resource management scheme using finalization, we would still face many other problems. The implementation would require the addition of some very complex, platform-specific code that would be difficult to get right, and tough to debug. Here are a few examples of some of the tricky issues that would need to be carefully coded around in each object's finalize method:
Operating system objects can have subtle limitations on when particular objects can be disposed. For example in the X Window System, a GC can contain a Font. If you dispose the Font before the GC, you leave the operating system in an indeterminate state. The following quote from The Java Language Specification hints at the complexity of the code that would be required to handle such limitations for each object finalizer:
"The Java programming language imposes no ordering on finalize method calls. Finalizers may be called in any order, or even concurrently." [JLS 12.6.2]
Add to this that for some operating systems, subtle platform behavior in this area may be undocumented and can change in future versions of the operating system.
On some platforms, operating system resources need to be disposed in the UI thread. According to The Java Language Specification:
"... the language does not specify which thread will invoke the finalizer for any given object." [JLS 12.6]
This means that SWT finalize methods would need to synchronize with the UI thread, which would mean that there would be a significant cost overhead for each dispose. In addition, given the previous point that disposal order is important for some resources, we think it is possible to get into a deadlock situation if we are trying to reorder multiple concurrent threads that are trying to synchronize with the UI thread.
Our final point against finalization is this: SWT keeps as much state in the operating system as possible to simplify the implementation (less code, smaller objects). If we were to use finalization, we could not store resources only in the operating system. We would have to hold on to them in Java code so that they would not get garbage collected. For example, when you set a Font into a Button, SWT sets the font resource in the OS button. The Button doesn't keep a reference to the font resource, because it doesn't need to. The state is in the operating system. In order to determine the Button's font, we query the OS. There is no point caching the font resource in Java code, because it just takes up space and complicates the Button code, which now has to make sure there are no stale cache problems.
Given that there are many problems when trying to use finalization to free resources, we think it’s dangerous to offer it as a solution in SWT, which is designed to expose operating system functionality. We believe it is reasonable to expect programs to explicitly free operating system resources. We see this as simply a fact of life because the current generation of operating systems do not support garbage collection.
You have created an SWT resource, so when do you free it? First of all, for many of the reasons mentioned above, we do not recommend that you use finalization to manage your SWT resources either. If you feel you absolutely must implement a finalize method in your classes, do so only as a "safety net" or "backup", and dispose your resource as usual. Remember to synchronize with the UI thread, as follows:
protected void finalize() { // NOT recommended
super.finalize();
display.asyncExec(new Runnable() {
public void run() {
if (!myResource.isDisposed()) {
myResource.dispose();
}
}
});
}
Even this minimal "safety net" use of finalizers is not recommended, because if memory is limited, your application could end up having to wait for a bunch of your finalize methods to run before the memory from those objects can be reclaimed.
The best way to manage SWT resources is to dispose them as soon as you are through with them. In the case of certain resource types, such as GC and Printer, you almost always create them and dispose them within the scope of the same method.This makes it easy to verify that the resource is being disposed properly, because you only have to go to one place to see the code to create the resource and the code to dispose it. For example:
GC gc = new GC(canvas);
gc.draw... // do some drawing on the canvas using the GC
gc.dispose();
In the case of resources that are set into a GC for drawing, you need to be careful not to dispose them while they are still set into the GC. Dispose the GC before the resource. For example:
Font font = new Font (display, "Courier", 10, SWT.NORMAL);
GC gc = new GC(canvas);
gc.setFont(font);
gc.drawText... // do some text drawing on the canvas using the GC
gc.dispose();
font.dispose();
Remember that you do not dispose the GC that is passed to you in a paint listener, because you did not allocate it!
public void paintControl(PaintEvent event) {
event.gc.draw...
// do NOT call event.gc.dispose();
}
If you are using graphics resources in a widget - for example, widget.setFont(font) - it is often best to clean these up when the widget they are used in is disposed, so you can hook a dispose listener on the widget as follows:
widget.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
font.dispose();
}
}
If resources are shared by multiple widgets (or other objects) in your application, then you may want to implement a reference-counting scheme in a "resource manager" class, which would keep track of the number of references to a resource, and dispose the resource when the reference count reaches 0. In this case, you would ask your resource manager for each resource you need, perhaps something like this:
Image image = myResourceManager.getNamedResource("imageName");
and the dispose listener on your widget(s) might look something like this:
widget.addDisposeListener(new DisposeListener() {
public void widgetDisposed(DisposeEvent e) {
myResourceManager.dispose(image);
}
});
Whether or not to use a reference-counting scheme, and how to implement it, are design decisions that must be made on a per-application basis.
Widgets themselves do not usually need to be disposed programmatically. A shell and its children are disposed when the user closes its window. There are a couple of places where shells are typically disposed programmatically: when the user selects File->Exit in an application window, or OK in a dialog. Here is an example of disposing a shell when File->Exit is selected:
// Create menu bar
Menu menuBar = new Menu(shell, SWT.BAR);
// Create File menu
MenuItem item = new MenuItem(menuBar, SWT.CASCADE);
item.setText("File");
Menu fileMenu = new Menu(shell, SWT.DROP_DOWN);
item.setMenu(fileMenu);
// Create File -> Exit menu item and add selection listener
item = new MenuItem(fileMenu, SWT.NULL);
item.setText("Exit");
item.addSelectionListener(new SelectionAdapter() {
public void widgetSelected(SelectionEvent event) {
shell.close(); // calls dispose() - see note below
}
});
Note that we actually call close() instead of dispose(). This gives the application a chance to cancel the close operation if necessary (for example, if something was not saved) inside of a shell listener. We could call dispose(), which would simply destroy the shell without invoking the shell listener, but it's better style to call close().
JFace provides a registry mechanism on top of SWT for image and font resource management. See the article Using Images in the Eclipse UI for more information on the JFace image management facilities.
[JB] Bloch, Joshua. Effective Java - Programming Language Guide. Addison-Wesley, Boston, 2001.
ISBN: 0-201-31005-8.
[JLS] Gosling James, Bill Joy, Guy Steele, Gilad Bracha. The Java Language Specification, Second Edition
http://java.sun.com/docs/books/jls/second_edition/html/j.title.doc.html