我正在寻找JSF应用程序中的内存泄漏。我已经阅读了各种文章,讨论在使用带有事务和会话的线程时可能发生的内存泄漏。以下代码是否会产生泄漏?此代码被调用数千次(对于页面请求)。
public class HibernateHelper
{
private static final ThreadLocal threadSession = new ThreadLocal();
private static final ThreadLocal threadTransaction = new ThreadLocal();
public static Session startSessionWithDBName(SessionFactorySingleton.DBname dbname)
{
Session s = getSessionWithDBName(dbname);
beginTransaction(dbname);
return s;
}
@SuppressWarnings("unchecked")
private static Session getSessionWithDBName(SessionFactorySingleton.DBname dbname)
{
Session s = (Session) threadSession.get();
if (s == null)
{
s = SessionFactorySingleton.getSessionFactory(dbname).openSession();
threadSession.set(s);
}
else
{
String sf = ((SessionFactoryImpl)s.getSessionFactory()).getProperties().getProperty("hibernate.session_factory_name");
if((dbname !=null && sf !=null) && !sf.equals(dbname.toString()))
{
closeSessionBySessionFactoryName(sf);
s = SessionFactorySingleton.getSessionFactory(dbname).openSession();
threadSession.set(s);
}
}
return s;
}
@SuppressWarnings("unchecked")
public static boolean isSessionOpen()
{
Session s = (Session) threadSession.get();
if (s != null)
return s.isOpen();
return false;
}
public static boolean wasTransactionCommited()
{
Transaction t = (Transaction) threadTransaction.get();
if (t != null)
return t.wasCommitted();
return false;
}
@SuppressWarnings("unchecked")
public static void closeSessionBySessionFactoryName(String sessionfactoryname) {
Session s = (Session) threadSession.get();
if (s != null && s.isOpen())
{
String sfname = ((SessionFactoryImpl)s.getSessionFactory()).getProperties().getProperty("hibernate.session_factory_name");
if(sfname.equals(sessionfactoryname))
{
try
{
commitTransaction(sfname);
} catch (Exception e) {
e.printStackTrace();
}
s.close();
threadSession.set(null);
}
}
}
/**
* Start a new database transaction.
*/
private static void beginTransaction(SessionFactorySingleton.DBname dbname) {
Session s = (Session) threadSession.get();
if(s!=null)
{
log.info("Starting new transaction for this thread.");
Transaction tx = s.beginTransaction();
threadTransaction.set(tx);
}
}
/**
* Commit the database transaction.
*/
public static void commitTransaction(String sessionfactoryname) throws Exception {
Transaction tx = (Transaction) threadTransaction.get();
try {
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
tx.commit();
log.info("Committed transaction for thread "+ Thread.currentThread().getName());
}
threadTransaction.set(null);
} catch (Exception e)
{
log.error("Exception when committing transaction: ", e);
rollbackTransactionKeepingSessionOpen();
throw e;
}
}
/**
* Rollback the database transaction.
*/
public static void rollbackTransactionKeepingSessionOpen() {
Transaction tx = (Transaction) threadTransaction.get();
threadTransaction.set(null);
if (tx != null && !tx.wasCommitted() && !tx.wasRolledBack()) {
tx.rollback();
}
}
}
答案 0 :(得分:1)
是的确如此
每个threadLocal实例都绑定到使用它的线程的生命周期
Tomcat的线程池很容易拥有数百个线程;每个线程都被重用并且永远不会被破坏
其中一些将被卡在I / O操作中,查询DB或诸如此类的东西,同时保持对开放Hibernate会话的引用(反过来可能包含许多附加实体)。
因此,在某些时期,应用程序可能会有许多无法通过GC回收的大对象图
我认为你应该以不同的方式管理Hibernate会话和事务。