Problem

One of the critical problems in the Online Only Bank data model from is the high potential for memory leaks. In fact, the process of garbage collection is not enough to protect you from memory leaks. Garbage collection protects you from only one kind of memory leak, known as a lost pointer. In languages such as C++, a memory leak resembles a lost pointer to an allocated block of memory. In this scenario, an object asks the runtime environment to allocate a block of memory. However, after this allocation, the pointer to the block is lost, perhaps through method return. There is a memory leak because there is a dead stack of allocated memory that the computer cannot use and to which the program can no longer get a pointer. In Java, this type of memory leak is not possible. However, Java suffers from a different type of memory leak based on the garbage-collection paradigm itself. Since all objects in Java are references, they form an intricate web of associations; these associations can cause a memory leak. Consider the JavaBean data class in Example 11-1.

Example 11-1. A basic JavaBean data class
package oracle.hcj.references;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
public class SomeDataClass {
 protected PropertyChangeSupport propertyChangeSupport =
 new java.beans.PropertyChangeSupport(this);
 private int age = 0;
 public void setAge(final int value) {
 final int oldAge = this.age;
 this.age = value;
 this.propertyChangeSupport.firePropertyChange("value", oldAge, this.age);
 }
 public int getAge( ) {
 return this.age;
 }
 public void addPropertyChangeListener(final PropertyChangeListener lst) {
 propertyChangeSupport.addPropertyChangeListener(lst);
 }
 public void removePropertyChangeListener(final PropertyChangeListener lst) {
 propertyChangeSupport.removePropertyChangeListener(lst);
 }
}


This class is a standard JavaBean with a single bound property named age. When the value of age changes, the listeners to the data object are informed of the change via normal property change events. Example 11-2 shows how the object is used in a GUI panel.

Example 11-2. A GUI panel that uses SomeDataClass
package oracle.hcj.references;
public class SomeGUIPanel extends JPanel implements ActionListener,
 PropertyChangeListener {
 public SomeDataClass dataObject = null;
 JTextField valueField;
 public SomeGUIPanel(final SomeDataClass obj) {
 this.setLayout(new GridLayout(1, 2));
 JLabel label = new JLabel("Age: ", SwingConstants.RIGHT);
 this.add(label);
 this.valueField = new JTextField( );
 this.valueField.addActionListener(this);
 this.add(this.valueField);
 changeObject(obj);
 }
 public void actionPerformed(final ActionEvent event) {
 if (event.getSource( ) == this.valueField) {
 try {
 int temp = Integer.parseInt(this.valueField.getText( ));
 this.dataObject.setAge(temp);
 } catch (final NumberFormatException ex) {
 Toolkit.getDefaultToolkit( ).beep( );
 }
 }
 }
 public void changeObject(final SomeDataClass obj) {
 this.dataObject = obj;
 this.dataObject.addPropertyChangeListener(this);
 this.valueField.setText(Integer.toString(obj.getAge( )));
 }
 public void propertyChange(final PropertyChangeEvent event) {
 if (event.getSource( ) == this.dataObject) {
 this.valueField.setText(Integer.toString(this.dataObject.getAge( )));
 }
 }
}


In this example, the GUI class gets a SomeDataClass object instance to manage at construction. However, it can also replace this instance with a different one. Furthermore, if someone else changes the data object, the GUI wants to be informed of that change so it can update its controls to reflect current data. Therefore, it registers itself as a property change listener with the data object to get these events. This code, which is fairly typical in GUI systems, has a glaring problem. SomeDataClass and SomeGUIPanel both hold onto a reference to each other and refuse to let go. The flow of the resulting program works like the following:

  1. The program starts up, and data objects are initialized.

  2. The SomeGUIPanel object is created inside a JFrame window and is passed one of many data objects in the program, which we'll call data object A.

  3. A user can now use the GUI panel.

  4. While the user is working, he presses a button in the frame window that calls changeObject( ) on the GUI panel with a new object, data object B, to manage.

  5. The user closes the GUI panel, and the program continues.

Voila! You now have a memory leak. Congratulations! At the end of the process, both data objects A and B are still holding onto the panel in their listener lists. Therefore, the GUI panel cannot be garbage collected by the virtual machine. Consequently, neither can any of its buttons, layout managers, string variables, and so on. Furthermore, since the responsibility for removing listeners from the data object does not rest with the data object itself, the memory will never be released. This Java memory leak does not result from a lost pointer, but because circular references pin objects in memory. Yes, this is a bug, and one that is very difficult to find. Yes, the programmer should have had the panel remove itself as a listener prior to changing objects or closing. Raise your hand if you've never written a bug. I wouldn't expect any honest software engineer to have his hand up. Professional Java code is plagued by these problems—in fact, it is plagued by them so much that there are several vendors that make software that helps you locate these kinds of errors (and they make a lot of money doing so). Performance-tuning software can help you find bugs such as this, but it can take time (and cost money). To make the situation worse, you must know how to use the profiler, and what should be in memory (and when). To find a memory leak like this, you will have to search through the program, method by method, and examine the contents of the memory at each step to see whether everything is in place. If this sounds like a scary proposition, your instincts are dead accurate. Single-line problems such as this can consume weeks of resources and man-hours. By using special techniques with references, you can prevent this problem before it starts. No, don't throw away that profiler; it's still quite useful. However, the less work you have to do with it, the faster you can place new features and requirements in a solid system. Your quest to get things right the first time will get a massive boost from coding with the java.lang.ref package.

      
Comments