【问题标题】:Memory leak caused by thread usage for sessions and transactions?会话和事务的线程使用导致内存泄漏?
【发布时间】:2014-07-17 03:29:35
【问题描述】:

我正在寻找 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();
            }
        }

}

【问题讨论】:

  • 使用分析器并找到产生内存泄漏的堆栈位置。

标签: java multithreading jsf tomcat memory-leaks


【解决方案1】:

是的。
每个 threadLocal 实例都绑定到使用它的线程的生命周期。
Tomcat的线程池可以轻松拥有数百个线程;每个线程都被重用并且永不被破坏。
其中一些会卡在 I/O 操作中,查询 DB 或诸如此类的东西,同时持有对打开的 Hibernate 会话的引用(该会话又可能包含许多附加实体)。
所以在某些时期,应用程序可能有许多无法被 GC 回收的大对象图。
我认为您应该以不同的方式管理 Hibernate 会话和事务。

【讨论】:

  • 谢谢你,尤里。您能否就如何更改上述代码以避免这种情况提出建议?
  • 检查 'SessionInView' 方法(会话/事务在 servlet 过滤器中管理)。很容易适应这个例子。同样在JBoss Seam,他们在 JSF 阶段监听器中做这些事情,所以它更细粒度和 JSF 风格
  • PS:如果您可以从 Tomcat 迁移到 TomEE,这应该很简单,并且使用 EJB Lite 它将带走所有这些样板文件
猜你喜欢
  • 1970-01-01
  • 2018-09-11
  • 2011-09-23
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
  • 2021-09-02
  • 2022-11-06
  • 1970-01-01
相关资源
最近更新 更多