Hi,
It is a known problem that 4.0.4 and EJB-3 leaks Hibernate lazy proxy classes generated by Javassist. The cache introduced in Javassist-3.4 (4.0.4 ships with Javassist-3.2) should solve the problem, but unfortunately doesn't. Probably because Hibernate uses a different MethodHandler for each new proxy and so you get a cache miss.
However it is possible to use Javassist-3.4 and supply a custom ClassLoaderProvider instance. It is important that this provide creates a new CL instance that just delegates. So the CL can be collected (and so can the proxy). Here is how to do that. The code can be put into a service mbean:
ProxyFactory.useCache = false; ProxyFactory.classLoaderProvider = new ClassLoaderProvider() { public ClassLoader get(ProxyFactory pf) { ClassLoader parent = getClassLoader(pf); ClassLoader cl = new ClassLoader(parent) { @Override public Class loadClass(final String className) throws ClassNotFoundException { try { return super.loadClass(className); } catch (ClassNotFoundException e) { // only allow loading of ProxyObject from this loader if (className.equals(ProxyObject.class.getName())) { return ProxyObject.class.getClassLoader().loadClass(className); } // was some other classname, throw the CNFE throw e; } } }; return cl; } private ClassLoader getClassLoader(ProxyFactory pf) { Class superClass = pf.getSuperclass(); Class[] interfaces = pf.getInterfaces(); ClassLoader loader = null; if (superClass != null && !superClass.getName().equals("java.lang.Object")) loader = superClass.getClassLoader(); else if (interfaces != null && interfaces.length > 0) loader = interfaces[0].getClassLoader(); if (loader == null) { loader = getClass().getClassLoader(); // In case javassist is in the endorsed dir if (loader == null) { loader = Thread.currentThread().getContextClassLoader(); if (loader == null) loader = ClassLoader.getSystemClassLoader(); } } return loader; } };