I noticed the problem in this post
http://forum.hibernate.org/viewtopic.php?t=930270
But I found it is NOT a problem of Application Server.  To prove this, I created two simple classes.  "CallJunk" repeatedly creates a URLClassLoader , loads Junk and hibernate classes, and call Junk.main(). Junk.main() opens a session and closes it. 
The CallJunk program grows 1-2 MB in each loop step and finally run out of memory. 
I profiled with JProfiler, the URLClassLoader and hibernate classes are not GCed. 
According to that post, I also tried upgrade CGLib to 2.2_beta1, no help; tried JDK1.5 and JDK1.4, no help; tried Introspector.flushCaches(), no help;  tried hibernate.cglib.use_reflection_optimize=false, no help; my simple junk class does not use ThreadLocal.
Code:
package calljunk;
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
public class CallJunk {
   static String[] dum=new String[0];
   static Class[] parameterTypes = new Class[]{dum.getClass()};
   public static void main(String[] args)throws Exception {
      URL[] urls = new URL[]{
            new File("../junk/cglib-2.2_beta1.jar").toURL(),
            new File("../junk/commons-collections-2.1.1.jar").toURL(),
            new File("../junk/dom4j-1.6.1.jar").toURL(),
            new File("../junk/ehcache-1.2.jar").toURL(),
            new File("../junk/hibernate3.jar").toURL(),
            new File("../junk/jta.jar").toURL(),
            new File("../junk/postgresql-8.0-311.jdbc3.jar").toURL(),
            new File("../junk/commons-logging-1.0.4.jar").toURL(),
            new File("../junk/bin").toURL()
         };
      
      for (int i=0; i<1000; i++) {
         System.out.println("No "+i+": ");
         //System.in.read();
         Class cls = new URLClassLoader(urls).loadClass("junk.Junk");
         cls.getMethod("main", parameterTypes).invoke(null, new Object[]{dum});
      }
   }
}
package junk;
import java.beans.Introspector;
import org.hibernate.HibernateException;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.cfg.Environment;
import javax.naming.InitialContext;
public class Junk {
    private static String CONFIG_FILE_LOCATION = "/hibernate.cfg.xml";
   public static void main(String[] args) throws Exception {
      Configuration cfg = new Configuration();
      SessionFactory sFactory = null;
      try {
         cfg.configure(CONFIG_FILE_LOCATION);
         String sessionFactoryJndiName = cfg.getProperty(Environment.SESSION_FACTORY_NAME);
         if (sessionFactoryJndiName != null) {
            cfg.buildSessionFactory();
            sFactory = (SessionFactory) (new InitialContext()).lookup(sessionFactoryJndiName);
         } else {
            sFactory = cfg.buildSessionFactory();
         }
      } catch (Exception e) {
         throw new HibernateException("Could not initialize the Hibernate configuration");
      }
       Session s = sFactory.getCurrentSession();
       s.close();
       Introspector.flushCaches();
       sFactory.close();
   }
   
}