【问题标题】:Oracle leaks threads on when closing Hibernate Sessions with Oracle JDBC driver使用 Oracle JDBC 驱动程序关闭休眠会话时,Oracle 泄漏线程
【发布时间】:2020-06-30 12:24:46
【问题描述】:

我们正在使用 Hibernate 运行一个多类加载器 Java 应用程序:

<dependency>
    <groupId>org.hibernate</groupId>
    <artifactId>hibernate-core</artifactId>
    <version>5.4.16.Final</version>
</dependency>
<dependency>
    <groupId>com.oracle.database.jdbc</groupId>
    <artifactId>ojdbc10-production</artifactId>
    <version>19.7.0.0</version>
    <type>pom</type>
</dependency>

我们注意到,即使关闭所有 Hibernate 和工件以及 unregister 驱动程序,我们仍然会看到线程保持活动状态,这些线程在 contextclassloader 中保存类加载器。有没有办法关闭这些线程TimerOracleTimeoutPollingThread

 public void close() throws IOException {
    sessionFactory.close();
    session.close();
    factory.close();

    try {
      Enumeration<Driver> de = DriverManager.getDrivers();
      while(de.hasMoreElements()) {
        Driver d = de.nextElement();
        if(d.getClass().getClassLoader() == RGHibernate.class.getClassLoader()) {
          DriverManager.deregisterDriver(d);
        }

      }
    } catch (SQLException e) {
      // TODO Auto-generated catch block
      e.printStackTrace();
    }
  }
}

【问题讨论】:

  • 您确定每个驱动程序都未注册吗?什么是 RGHibernate 类?
  • RGHibernate 是实现 close() 方法的方法,它关闭所有休眠的 close() 方法。并在当前的 URLClassLoader 中加载。
  • @SabareeshMuralidharan 是的,我确认它已取消注册(),但一个实例由 OracleDriver 中的静态字段保存为默认驱动程序
  • 我猜有一个已知的错误。静态字段
  • 有没有办法以某种方式关闭这个默认驱动程序?

标签: java oracle hibernate classloader contextclassloader


【解决方案1】:
    protected void cancelTimers() {
        try {
            for (Thread thread : Thread.getAllStackTraces().keySet())
                if (thread.getClass().getSimpleName().equals("TimerThread"))
                    cancelTimer(thread);
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private void cancelTimer(Thread thread) throws Exception {
        // Timer::cancel
        Object queue = ReflectionUtils.getFieldValue(thread, "queue");
        Method m = queue.getClass().getDeclaredMethod("isEmpty");
        m.setAccessible(true);
        if ((boolean) m.invoke(queue)) {
            synchronized (queue) {
                ReflectionUtils.setFieldValue(thread, "newTasksMayBeScheduled", false);
                m = queue.getClass().getDeclaredMethod("clear");
                m.setAccessible(true);
                m.invoke(queue);
                queue.notify();
            }
        }
    }

【讨论】:

  • 它是否也与 OralceTImeoutPollingThread 有关?
  • OracleTimeoutPollingThread会在OracleDriverDriverManager注销时停止,也可以直接调用OracleTimeoutThreadPerVM.stopWatchdog()
  • 正如您在问题中看到的,我们确实调用了 DriverManager.deregisterDriver(d);并且 OracleTimeoutPollingThread 确实停止了,但 Timer 仍然存在并持有 OracleTimeoutPollingThread 引用
  • 是oracle jdbc驱动的bug,应该在HAManager.stop()中调用timer.cancel()
  • ``` className = "oracle.jdbc.driver.NoSupportHAManager";对象 noSupportHAManager = Class.forName(className).getMethod("getInstance").invoke(null); if (noSupportHAManager != null) { Field f = Class.forName("oracle.jdbc.driver.HAManager").getDeclaredField("timer"); f.setAccessible(true); timer = (Timer) f.get(noSupportHAManager); if (timer != null) timer.cancel(); } ```
猜你喜欢
  • 2012-11-25
  • 2014-11-28
  • 2014-12-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-05
  • 2016-05-27
相关资源
最近更新 更多