Item 06 - Eliminate obsolete object references

From Effective Java 2/e by Joshua Bloch

Garbage-collected language can easily lead to the impression that you don’t have to think about memory management, but this isn’t quite true.

Unintentional Object Retention Example

// Can you spot the "memory leak"?
public class Stack {
   private Object[] elements;
   private int size = 0;
   private static final int DEFAULT_INITIAL_CAPACITY = 16;
   public Stack() {
      elements = new Object[DEFAULT_INITIAL_CAPACITY];
   }

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

   public Object pop() {
      if (size == 0)
         throw new EmptyStackException();

      // memory leak
      return elements[--size];

      // fix memory leak
      // Object result = elements[size];
      // elements[size] = null;
      // return result;
   }

   /**
   * 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)
         elements = Arrays.copyOf(elements, 2 * size + 1);
   }
}
  • Common source of memory leak
    • Cache
    • Listeners and Callbacks
  • Use WeakReference to avoid memory leak
  • Typically discovered by
    • careful code inspection
    • aid of a debugging tool known as a "Heap Profiler"
// http://www.javalobby.org/java/forums/t19468.html
// @author Santhosh Kumar T - santhosh@in.fiorano.com
public class WeakPropertyChangeListener implements PropertyChangeListener{
    WeakReference listenerRef;
    Object src;

    public WeakPropertyChangeListener(PropertyChangeListener listener, Object src){
        listenerRef = new WeakReference(listener);
        this.src = src;
    }

    public void propertyChange(PropertyChangeEvent evt){
        PropertyChangeListener listener = (PropertyChangeListener)listenerRef.get();
        if(listener==null){
            removeListener();
        }else
            listener.propertyChange(evt);
    }

    private void removeListener(){
        try{
            Method method = src.getClass().getMethod("removePropertyChangeListener"
                    , new Class[] {PropertyChangeListener.class});
            method.invoke(src, new Object[]{ this });
        } catch(Exception e){
            e.printStackTrace();
        }
    }
}

Posted by The Finest Artist