Finalizers are even eviler than you think

Posted by    |      

Developerworks is featuring the best article I have ever read on the subject of Java performance. The authors dispose of the canard that temporary object creation is expensive in Java, by explaining how generational garbage collection works in the Sun JVM (this is a bit more detailed explanation than the typical one, by the way). Well, I already knew this; Hibernate rejected the notion of object pooling right from the start (unfortunately, the EJB spec has not yet caught up).

What I did /not/ know was that objects which implement finalize() require two full garbage collection cycles to be released. Now, everyone knows that finalize() cannot be relied upon and we should not write important code in a finalizer. But /this/ finalize() method, taken from Hibernate's SessionImpl class seemed like a really good idea:

/**
 * Just in case user forgot to call close()
 */
protected void finalize() throws Throwable {
  
  log.debug("running Session.finalize()");
  
  if (isCurrentTransaction) log.warn("afterTransactionCompletion() was never called");
  
  if (connection!=null) { //ie it was never disconnected
    if ( connection.isClosed() ) {
      log.warn("finalizing unclosed session with closed connection");
    }
    else {
      log.warn("unclosed connection");
      if (autoClose) connection.close();
    }
  }
}

The main thing that this method is doing is checking to see if the naughty application forgot to close the session and, if so, log a WARN. This is a really good idea! It is otherwise quite hard to noticed unclosed sessions, and the JDBC connections they own. Unfortunately it has the terrible side-effect of preventing the session from being garbage collected immediately. Now, even after reading the article, I didn't think that this would be such a big deal, since I dereference almost all of the session's state from close(). However, My performance tests are showing a really /big/ difference in performance, just from removing the finalizer. For one problematic test, I actually /halved/ the overhead of Hibernate!

I can barely believe this result, but I've been successfully reproducing it for the last two hours.


Back to top