2014-02-26 64 views
2

我在GlassFish上運行我的應用程序,我使用Spring Security和Hibernate。 當我運行該應用程序時,GlassFish控制檯上將顯示以下警告和錯誤。我怎樣才能避免它們?爲什麼我收到JDBC驅動程序警告和ThreadLocal錯誤?

WARNING: The web application [] registered the JDBC driver [com.mysql.jdbc.Driver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered. 
SEVERE: The web application [] created a ThreadLocal with key of type [java.lang.ThreadLocal] (value [[email protected]]) and a value of type [org.hibernate.internal.SessionImpl] (value [SessionImpl(PersistenceContext[entityKeys=[],collectionKeys=[]];ActionQueue[insertions=[] updates=[] deletions=[] collectionCreations=[] collectionRemovals=[] collectionUpdates=[] unresolvedInsertDependencies=UnresolvedEntityInsertActions[]])]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak. 
SEVERE: The web application [] created a ThreadLocal with key of type [net.sf.json.AbstractJSON$1] (value [[email protected]]) and a value of type [java.util.HashSet] (value [[]]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak. 
SEVERE: The web application [] created a ThreadLocal with key of type [net.sf.json.AbstractJSON$1] (value [[email protected]]) and a value of type [java.util.HashSet] (value [[]]) but failed to remove it when the web application was stopped. Threads are going to be renewed over time to try and avoid a probable memory leak. 

的hibernate.cfg.xml

<hibernate-configuration> 

    <session-factory> 

     <!-- Database connection settings --> 
     <property name="connection.driver_class"> 
      com.mysql.jdbc.Driver 
     </property> 
     <property name="connection.url"> 
      jdbc:mysql://localhost:3306/myproject 
     </property> 
     <property name="connection.username">root</property> 
     <property name="connection.password"></property> 

     <!-- JDBC connection pool (use the built-in) --> 
     <property name="connection.pool_size">12</property> 

     <!-- SQL dialect --> 
     <property name="dialect"> 
      org.hibernate.dialect.MySQLDialect 
     </property> 



     <!-- Enable Hibernate's automatic session context management --> 
     <property name="current_session_context_class">thread</property> 

<!--   Disable the second-level cache --> 

<!-- <property name="cache.provider_class"> 
      org.hibernate.cache.EhCacheProvider 
     </property> 

     <property name="hibernate.cache.use_query_cache">true</property>--> 


     <!-- Echo all executed SQL to stdout --> 
     <property name="show_sql">true</property> 

HibernateUtil.java

public class HibernateUtil { 

    private static ServiceRegistry serviceRegistry; 
    private static final ThreadLocal<Session> threadLocal = new ThreadLocal(); 
    private static SessionFactory sessionFactory; 

    private static SessionFactory configureSessionFactory() { 
     try { 
      Configuration configuration = new Configuration(); 
      configuration.configure(); 
      serviceRegistry = new ServiceRegistryBuilder().applySettings(configuration.getProperties()).buildServiceRegistry(); 

      sessionFactory = configuration.buildSessionFactory(serviceRegistry); 

      return sessionFactory; 
     } catch (HibernateException e) { 
      System.out.append("** Exception in SessionFactory **"); 
      e.printStackTrace(); 
     } 
     return sessionFactory; 
    }  

    static { 
    try { 
     sessionFactory = configureSessionFactory(); 
    } catch (Exception e) { 
     System.err.println("%%%% Error Creating SessionFactory %%%%"); 
     e.printStackTrace(); 
    } 
    } 

    private HibernateUtil() { 
    } 

    public static SessionFactory getSessionFactory() { 
    return sessionFactory; 
    } 

    public static Session getSession() throws HibernateException { 
    Session session = threadLocal.get(); 

    if (session == null || !session.isOpen()) { 
     if (sessionFactory == null) { 
     rebuildSessionFactory(); 
     } 
     session = (sessionFactory != null) ? sessionFactory.openSession() : null; 
     threadLocal.set(session); 
    } 

    return session; 
    } 

    public static void rebuildSessionFactory() { 
    try { 
     sessionFactory = configureSessionFactory(); 
    } catch (Exception e) { 
     System.err.println("%%%% Error Creating SessionFactory %%%%"); 
     e.printStackTrace(); 
    } 
    } 

    public static void closeSession() throws HibernateException { 
    Session session = (Session) threadLocal.get(); 
    threadLocal.set(null); 

    if (session != null) { 
     session.close(); 
    } 
    } 
} 
+1

我從個人的經驗中知道,關於JDBCDriver的一個不值得擔心。基本上,這意味着您在程序結束之前沒有關閉數據庫連接。 – JamesENL

+0

隨着你的配置,Spring註銷驅動程序,請諮詢Spring團隊爲什麼不這樣做。 –

+0

請參閱[this](http://stackoverflow.com/q/3320400/1391249)問題。 – Tiny

回答

8

這些是可以在應用程序重新部署的情況下發生的錯誤消息而服務器仍在運行。

如果是關機場景或開發重新部署,這些消息可以安全地忽略,但只有在生產中需要重新部署時纔會變得非常重要,這很少見。即使在生產中,大部分時間我們都想停止服務器進程並完全重新啓動服務器進程。這是在每個消息的意思的一些細節:

消息1 - 駕駛員未註銷應用程序時停止:

警告:[]中登錄的JDBC驅動 [com.mysql網絡應用.jdbc.Driver],但在網絡應用程序停止時未能取消註冊。爲防止內存泄漏,JDBC驅動程序 被強制取消註冊。

JDBC驅動程序在啓動時在JVM級別以單例方式註冊,這意味着要由服務器通過將驅動程序jar發佈到服務器級別的文件夾中來完成。

在這種情況下,應用程序似乎攜帶驅動程序本身,這不是驅動程序意圖部署的方式。

要解決此問題,請從應用程序中刪除驅動程序,並將其註冊到服務器級別。如果多個應用程序具有相同的驅動程序,這也會導致內存泄漏 - 同樣參見answer

消息2 - 的ThreadLocal沒有清理:

重度:Web應用程序[]創建一個ThreadLocal與 類型[java.lang.ThreadLocal中](值密鑰[java.lang.ThreadLocal中@ 1087985b]) 和[org.hibernate.internal.SessionImpl]類型的值,但當Web應用程序停止時, 未能將其刪除。線程是 會隨着時間的推移而被更新以避免可能的內存泄漏。

這意味着,一個應用程序線程彈簧存儲在線程Hibernate會話(每個線程作爲數據存儲,其中的東西可以通過ThreadLocal附)。

但是,線程在應用程序重新啓動時沒有清理會話,所以在重新部署後線程被重用時,存儲在該線程中的此變量可以可見。

這可能令人驚訝,但會話指向其他對象的最糟糕的地方,它們本身指向類,它們在重新部署之前指向舊的類加載器。

這意味着對象樹的很大一部分將不會被垃圾收集,因爲這會泄漏到以前部署的對象的「鏈接」。結果是ClassLoader Memory Leak

該消息表示,這種情況可能是由於未被清理的ThreadLocals而發生的,而且一些預防措施將會落實到位(開始殺死線程並創建新線程而不是池化,以擺脫泄漏的線程本地人) 。

總之,如果您不需要在生產環境中重新部署並始終重新啓動服務器,則可以安全地忽略這些消息。

+0

非常感謝您的全面回答,我還有一個問題,您建議使用哪種軟件進行測試? Java Web應用程序? – AlexCartio1

+0

就我而言,我需要在生產中重新部署。有沒有辦法可以手動清理這些threadlocals? – mpmp

2

爲了擺脫JDBC驅動程序的警告,運行應用程序關閉如下:

String url = "your JDBC url"; 
Driver driver = DriverManager.getDriver(url); 
DriverManager.deregisterDriver(driver); 

如果您使用的是Spring beans,則可以將其放入DisposableBeandestroy()方法中。在web.xml

public class CleanupListener implements ServletContextListener { 

    public void contextDestroyed(ServletContextEvent arg0) { 
     // enter cleanup code here 
    } 

    public void contextInitialized(ServletContextEvent arg0) { } 
} 

設置它:

在Servlet環境,你可以使用一個ServletContextListener

<listener> 
    <listener-class>com.example.CleanupListener</listener-class> 
</listener> 
+0

謝謝,當應用程序關閉時運行此功能意味着什麼?第二個問題呢? – AlexCartio1

+0

@ AlexCartio1您可以在應用程序關閉時調用的webapp中註冊監聽器,您可以在其中放置清理代碼。對於Spring應用程序,您可以使用'DisposableBean'。如果不是Spring,但在servlet環境中,可以使用'ServletContextListener'。 – holmis83

+0

你會給我一個這樣的聽衆的例子嗎? – AlexCartio1

1

如果在沒有Class.forName()方法的類路徑中找到任何Driver類,並且它可能會導致此類錯誤消息,那麼JDK6將自動加載JDBC驅動程序。最好爲應用程序編寫一個Listener,並在應用程序關閉時取消註冊每個驅動程序。您可以使用DiverManager#getDrivers()方法獲取所有已註冊的驅動程序,並可以逐個註銷。

+0

你應該給我一個這樣的聽衆的例子嗎? – AlexCartio1

+1

請使用ServletContextListener進行測試,並在上下文銷燬時取消註冊您的驅動程序。您可以在http://www.mkyong找到ServletContextListener的示例示例。com/servlet/what-is-listener-servletcontextlistener-example/... –

相關問題