Help

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.

8 comments:
 
18. Feb 2004, 06:50 CET | Link
Glen Stampoultzis
I imagine there's a lot of finalizers in various JDBC drivers. Shame you can't really control that.
ReplyQuote
 
19. Feb 2004, 03:48 CET | Link
The same discovery was made in the NIO package. I believe that a similar check was done in finalizers for (at least certain types of) Channel implementations of the early 1.4 releases.

This turned out to be so detrimental for performance in big benchmarks that it was removed.

Mark Reinhold mentioned this in a talk he did a while back, and said that people just have to make absolutely sure that they close Channels when they're done with them.
 
19. Feb 2004, 14:51 CET | Link
Anonymous Guest
Does this blog entry imply that you are going to be refactoring Hibernate to eliminate the need for SessionImpl's finalize() method?

 
19. Feb 2004, 16:25 CET | Link
FYI: I was under the impression you can achieve the same effect as a finalizer with a WeakReference and a ReferenceQueue but without the performance hit to the garbage collector that a finalize() method has.
 
19. Feb 2004, 20:28 CET | Link
Tom Copeland | tom(AT)infoether.com
Yup. We've got a whole ruleset on PMD dedicated to finalizer pitfallls:

http://pmd.sourceforge.net/rules/finalizers.html

Tom


 
26. Feb 2004, 14:41 CET | Link
Gavin,

we also run into this problem.

We are pretty sure that we close our connections as every openSession and session.close() is done in generated code with the standard try / catch / finally structure.

Because finalize() is executed asynchronously by the JVM it is hard to track which sesion is being closed.

It would be a great help if we could mark our sessions with our own ID in sessionfactory.openSession("sessionID"), if this would be printed in the "unclosed session" message we hava at least an idea of where the problem started...
 
14. Mar 2004, 18:42 CET | Link
Juozas Baliuka
It is better to have "DebugSessionDecorator" and to remove finalizer.
For debug decorator you can store stack trace, put it to "unclosed" set before to return for user ( close removes session from this set ) and you can print "unclosed" set with stack trace at any time.
 
06. May 2004, 20:45 CET | Link
Seth Ladd
Does this mean Hibernate will remove this finalizer? I always wondered why it had this, due to all that literature saying finalizers are bad.
Post Comment