为什么我收到JDBC驱动程序警告和ThreadLocal错误?

时间:2014-02-26 04:50:02

标签: java hibernate struts2 spring-security glassfish-4

我在GlassFish上运行我的应用程序,我使用Spring Security和Hibernate。 当我运行应用程序时,GlassFish控制台上将显示以下警告和错误。我该如何避免它们?

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 [java.lang.ThreadLocal@1087985b]) 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 [net.sf.json.AbstractJSON$1@362386d7]) 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 [net.sf.json.AbstractJSON$1@362386d7]) 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();
    }
  }
}

3 个答案:

答案 0 :(得分:8)

这些是在服务器保持运行时应用程序重新部署时可能发生的错误消息。

如果是关闭方案或开发重新部署,可以安全地忽略这些消息,只有在生产中需要重新部署时,这些消息才变得很重要,这种情况很少见。大多数时候即使在生产中我们也想停止服务器进程并完全重启它。这是每条消息的一些细节含义:

消息1 - 应用程序停止时未取消注册驱动程序:

  

警告:Web应用程序[]注册了JDBC驱动程序   [com.mysql.jdbc.Driver]但无法在网络上取消注册   申请被停止了。为防止内存泄漏,JDBC驱动程序具有   被强行未登记。

JDBC驱动程序在启动时在JVM级别的单例中注册,这意味着服务器通过在服务器级别的文件夹中发布驱动程序jar来完成。

在这种情况下,应用程序似乎带有驱动程序本身,这不是驱动程序的部署方式。

要解决此问题,请从应用程序中删除驱动程序,并将其注册到服务器级别。如果多个应用程序具有相同的驱动程序,这也会导致内存泄漏 - 请参阅此answer

消息2 - 未清除ThreadLocal:

  

SEVERE:Web应用程序[]使用键创建了一个ThreadLocal   type [java.lang.ThreadLocal](value [java.lang.ThreadLocal@1087985b])   和[org.hibernate.internal.SessionImpl]类型的值但是   Web应用程序停止时无法删除它。线程是   随着时间的推移需要更新,以避免可能的内存泄漏。

这意味着一个应用程序spring线程在线程中存储了一个Hibernate会话(每个线程作为数据存储,可以通过ThreadLocal附加事物)。

但是当重新启动应用程序时,线程没有清理会话,因此当重新部署线程重用时,存储在线程中的这个变量可以看到。

这可能会令人惊讶,但最糟糕的是会话指向其他对象,这些对象本身指向类,在重新部署之前指向旧的类加载器。

这意味着对象树的大部分将不会被垃圾收集,因为这个泄漏的“链接”指向前一个部署的对象。结果是ClassLoader Memory Leak

消息说这个场景可能由于未清理的ThreadLocals而发生,并且将采取一些预防措施(开始杀死线程并创建新线程而不是池化,以摆脱泄漏的线程本地)。

总结一下,如果您不需要在生产中重新部署并始终重新启动服务器,则可以安全地忽略这些消息。

答案 1 :(得分:2)

要删除JDBC驱动程序警告,请在应用程序关闭时运行以下命令:

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

如果您使用的是Spring bean,可以将其放在DisposableBeandestroy()方法中。

在servlet环境中,您可以使用ServletContextListener

public class CleanupListener implements ServletContextListener {

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

    public void contextInitialized(ServletContextEvent arg0) { }
}

在web.xml中设置:

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

答案 2 :(得分:1)

如果在没有Class.forName()方法的类路径中找到任何Driver类,那么JDK6将自动加载JDBC驱动程序,并且它可能会导致此类错误消息。最好为您的应用程序编写一个监听器,并在应用程序关闭时取消注册每个驱动程序。您可以使用DiverManager#getDrivers()方法获取所有注册的驱动程序,并可以逐个取消注册。