Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.

...

The vector object in the noncompliant code example leaks memory. The condition for removing the vector element is mistakenly written as n > 0 instead of n >= 0. Consequently, the method fails to remove one element per invocation and quickly exhausts the available heap space.

Code Block
bgColor#FFCCCC

public class Leak {
  static Vector vector = new Vector();

  public void useVector(int count) { 	
    for (int n = 0; n < count; n++) {
      vector.add(Integer.toString(n));
    }
    // ...
    for (int n = count - 1; n > 0; n--) { // Free the memory
      vector.removeElementAt(n);
    }	
  }

  public static void main(String[] args) throws IOException {
    Leak le = new Leak();
    int i = 1;
    while (true) {
      System.out.println("Iteration: " + i);
      le.useVector(1);
      i++;
    }
  }
}

...

This compliant solution corrects the mistake by changing the loop condition to n >= 0. It also wraps the cleanup code in a finally block, so that it still executes even if the interim code throws an exception.

Code Block
bgColor#ccccff

public void useVector(int count) { 	
  for (int n = 0;
  try {
    for (; n < count; n++) {
      vector.add(Integer.toString(n));
    }
    // ...
  } finally {
    for (int n = countn - 1; n >= 0; n--) {
      vector.removeElementAt(n);
    }
  }
}	

Compliant Solution (clear())

Prefer the use of standard language semantics where possible. This compliant solution uses the vector.clear() method, which removes all elements.

Code Block
bgColor#ccccff

public void useVector(int count) { 	
  try {
    for (int n = 0; n < count; n++) {
      vector.add(Integer.toString(n));
    }
    // ...
  } finally {
    vector.clear(); // Clear the vector
  }
}

Noncompliant Code Example (Nonlocal Instance Field)

This noncompliant code example declares and allocates a HashMap instance field that is used only in the doSomething() method.

Code Block
bgColor#FFCCCC

public class Storer {
  private HashMap<Integer,String> hm = new HashMap<Integer, String>();
  
  private void doSomething() {
    // hm is used only here and never referenced again
    hm.put(1, "java");
    // ...
  }
}

...

This compliant solution declares the HashMap as a local variable within the doSomething() method. The hm local variable is eliminated after the method returns. When the local variable holds the only reference to the HashMap, the garbage collector can reclaim its associated storage.

Code Block
bgColor#ccccff

public class Storer {
  private void doSomething() {
    HashMap<Integer,String> hm = new HashMap<Integer,String>();
    hm.put(1,"java");
    // ...
  }
}

...

This noncompliant code example, known as the Lapsed Listener [Goetz 2005a], demonstrates unintentional object retention. The button continues to hold a reference of the reader object after completion of the readSomething() method, even though the reader object is never used again. Consequently, the garbage collector cannot collect the reader object. A similar problem occurs with inner classes because they hold an implicit reference to the enclosing class.

Code Block
bgColor#FFCCCC

public class LapseEvent extends JApplet {
  JButton button;
  public void init() {
    button = new JButton("Click Me");
    getContentPane().add(button, BorderLayout.CENTER);
    Reader reader = new Reader();
    button.addActionListener(reader);
    try {
      reader.readSomething();
    } catch (IOException e) { 
      // Handle exception 
    }		 
  }
}

class Reader implements ActionListener {
  public void actionPerformed(ActionEvent e)  {
    Toolkit.getDefaultToolkit().beep();
  }
  public void readSomething() throws IOException {
    // Read from file
  }
}

...

This noncompliant code example attempts to remove the reader through use of the removeActionListener() method.

Code Block
bgColor#FFCCCC

Reader reader = new Reader();
button.addActionListener(reader);
try {
  reader.readSomething();  // Can skip next line of code
  // Dereferenced, but control flow can change
  button.removeActionListener(reader);  
} catch (IOException e) { 
  // Forward to handler 
}

...

This compliant solution uses a finally block to ensure that the reader object's reference is removed.

Code Block
bgColor#ccccff

Reader reader = new Reader();
button.addActionListener(reader);
try {
  reader.readSomething();
} catch (IOException e) { 
  // Handle exception 
} finally {
  button.removeActionListener(reader);  // Always executed
}

...

This noncompliant code example implements a stack data structure [Bloch 2008] that continues to hold references to elements after they have been popped off the stack.

Code Block
bgColor#FFCCCC

public class Stack {
  private Object[] elements;
  private int size = 0;

  public Stack(int initialCapacity) {
    this.elements = new Object[initialCapacity];
  }

  public void push(Object e) {
    ensureCapacity();
    elements[size++] = e;
  }

  public Object pop() { // This method causes memory leaks
    if (size == 0) {
      throw new EmptyStackException();
    }
    return elements[--size];
  }

  /*
   * Ensure space for at least one more element, roughly
   * doubling the capacity each time the array needs to grow.
   */
  private void ensureCapacity() {
    if (elements.length == size) {
      Object[] oldElements = elements;
      elements = new Object[2 * elements.length + 1];
      System.arraycopy(oldElements, 0, elements, 0, size);
    }
  }
}

...

This compliant solution assigns null to all obsolete references.

Code Block
bgColor#ccccff

public Object pop() {
  if (size == 0) {
    throw new EmptyStackException(); // Ensures object consistency
  }
  Object result = elements[--size];
  elements[size] = null; // Eliminate obsolete reference
  return result;
} 

...

A common variation of the obsolete object fallacy is the unintentional retention of objects in collections such as maps. In this noncompliant code example, a server maintains temporary metadata about all committed secure connections.

Code Block
bgColor#FFCCCC

class HashMetaData {
  private Map<SSLSocket, InetAddress> m = Collections.synchronizedMap(
      new HashMap<SSLSocket, InetAddress>());

  public void storeTempConnection(SSLSocket sock, InetAddress ip) {
    m.put(sock, ip);  
  }

  public void removeTempConnection(SSLSocket sock) {
    m.remove(sock);  
  }	
}

...

This compliant solution uses weak references to allow timely garbage collection.

Code Block
bgColor#ccccff

// ...
private Map<SSLSocket, InetAddress> m = Collections.synchronizedMap(
  new WeakHashMap<SSLSocket, InetAddress>()
);

...

When the garbage collector clears the reference to an object, it adds the corresponding WeakReference object to the reference queue. The WeakReference object remains in the reference queue until some operation is performed on the queue (such as a put() or remove()). After such an operation, the WeakReference object in the hash map is also garbage-collected. Alternatively, this two-step procedure can be carried out manually by using the following code:

Code Block
bgColor#ccccff

class HashMetaData {
  private Map<WeakReference<SSLSocket>, InetAddress> m = 
      Collections.synchronizedMap(
        new HashMap<WeakReference<SSLSocket>, InetAddress>());
  ReferenceQueue queue = new ReferenceQueue();
  
  public void storeTempConnection(SSLSocket sock, InetAddress ip) {
    WeakReference<SSLSocket> wr = new WeakReference<SSLSocket>(sock, queue);

    // poll for dead entries before adding more 
    while ((wr = (WeakReference) queue.poll()) != null) {
      // Removes the WeakReference object and the value (not the referent) 
      m.remove(wr); 
    }  
    m.put(wr, ip);
  }

  public void removeTempConnection(SSLSocket sock) {
    m.remove(sock);  
  }
}

...

Use of soft references is also permitted. Soft references guarantee that the referent will be reclaimed before an OutOfMemoryError occurs and also that the referent will remain live until memory begins to run out.

Code Block
bgColor#ccccff

class HashMetaData {
  private Map<SoftReference<SSLSocket>, InetAddress> m = 
        Collections.synchronizedMap(
        new HashMap<SoftReference<SSLSocket>, InetAddress>());
  ReferenceQueue queue = new ReferenceQueue();

  public void storeTempConnection(SSLSocket sock, InetAddress ip) {
    SoftReference<SSLSocket> sr = new SoftReference<SSLSocket>(sock, queue);
    while ((sr = (SoftReference) queue.poll()) != null) {
      // Removes the WeakReference object and the value (not the referent)
      m.remove(sr); 
    }  
    m.put(sr, ip);
  }

  public void removeTempConnection(SSLSocket sock) {
    m.remove(sock);  
  }	
}

...

[API 2006]

Class Vector, Class WeakReference

[Bloch 2008]

Item 6. Eliminate obsolete object references

[Commes 2007]

Memory Leak Avoidance

[Goetz 2005a]

Lapsed Listeners

[Goetz 2005b]

Memory Leaks with Global Maps; Reference Queues

[Gupta 2005]

 

 

      49. Miscellaneous (MSC)