如何修复“web应用程序[myapp]似乎启动了一个名为[hikaripool-113 househeeper]的线程,但未能阻止它”?

ohtdti5x  于 2021-07-26  发布在  Java
关注(0)|答案(0)|浏览(593)

我正在运行一个springboot项目,我有一个调用sqlserver数据库的方法,它连接良好并返回结果。
但是,当我开始重新部署更新时,这会出现在tomcat日志中:

==> catalina.2021-04-25.log <==
25-Apr-2021 09:05:45.307 WARNING [Catalina-utility-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesJdbc The web application [ciamdev] registered the JDBC driver [com.microsoft.sqlserver.jdbc.SQLServerDriver] but failed to unregister it when the web application was stopped. To prevent a memory leak, the JDBC Driver has been forcibly unregistered.
25-Apr-2021 09:05:45.309 WARNING [Catalina-utility-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [ciamdev] appears to have started a thread named [mssql-jdbc-shared-timer-core-0] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:
 sun.misc.Unsafe.park(Native Method)
 java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
 java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1081)
 java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:809)
 java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1074)
 java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1134)
 java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
 java.lang.Thread.run(Thread.java:748)
25-Apr-2021 09:05:45.310 WARNING [Catalina-utility-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [ciamdev] appears to have started a thread named [HikariPool-113 housekeeper] but has failed to stop it. This is very likely to create a memory leak.
25-Apr-2021 09:05:45.312 WARNING [Catalina-utility-2] org.apache.catalina.loader.WebappClassLoaderBase.clearReferencesThreads The web application [ciamdev] appears to have started a thread named [HikariPool-113 connection adder] but has failed to stop it. This is very likely to create a memory leak. Stack trace of thread:

基本上有四个主要事件:

com.microsoft.sqlserver.jdbc.SQLServerDriver is forcibly removed
mssql-jdbc-shared-timer-core-0 thread was unable to shut down
HikariPool-113 housekeeper thread was unable to shut down
HikariPool-113 connection adder thread was unable to shut down

当我不断调用数据库,然后重新部署时,hikaripool线程开始成倍增加。
经过一番研究,我决定尝试一种涉及servletcontextlistener的解决方案
我现在发布相关信息/代码:
这是tomcat版本:

Using CATALINA_BASE:   /apps/tomcat/apache-tomcat-9.0.34
Using CATALINA_HOME:   /apps/tomcat/apache-tomcat-9.0.34
Using CATALINA_TMPDIR: /apps/tomcat/apache-tomcat-9.0.34/temp
Using JRE_HOME:        /usr
Using CLASSPATH:       /apps/tomcat/apache-tomcat-9.0.34/bin/bootstrap.jar:/apps/tomcat/apache-tomcat-9.0.34/bin/tomcat-juli.jar
Server version: Apache Tomcat/9.0.34
Server built:   Apr 3 2020 12:02:52 UTC
Server number:  9.0.34.0
OS Name:        Linux
OS Version:     3.10.0-1160.24.1.el7.x86_64
Architecture:   amd64
JVM Version:    1.8.0_275-b01
JVM Vendor:     Red Hat, Inc.
Apache Maven version 3.8.1

以下是我的pom.xml依赖项(我在另一篇文章中添加了hikaricp exclusions标记):

<dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>        

        <dependency>
            <groupId>com.squareup.okhttp3</groupId>
            <artifactId>okhttp</artifactId>
            <version>3.11.0</version>
        </dependency>

        <dependency>
            <groupId>com.jcraft</groupId>
            <artifactId>jsch</artifactId>
            <version>0.1.55</version>
        </dependency>

        <dependency>
            <groupId>com.googlecode.json-simple</groupId>
            <artifactId>json-simple</artifactId>
            <version>1.1.1</version>
        </dependency>

        <dependency>
            <groupId>com.microsoft.sqlserver</groupId>
            <artifactId>mssql-jdbc</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>com.zaxxer</groupId>
                    <artifactId>HikariCP</artifactId>
                </exclusion>
            </exclusions>            
        </dependency>      

    </dependencies>

我创建了一个db类,在这个类中我示例化了jdbctemplate并添加了数据源,还实现了servletcontextlistener,我敢肯定我搞砸了整个类,下面是这个类:

public class Db implements ServletContextListener {

  String message;

  @Override
  public void contextInitialized(ServletContextEvent servletContextEvent) {
    message = "contextInitialized\n\n";
    System.out.println("contextInitialized Server stopped");
  }

  @Override
  public void contextDestroyed(ServletContextEvent servletContextEvent) {
    message += "contextDestroyed\n\n";
    System.out.println("contextDestroyed Server stopped");

    Enumeration<Driver> drivers = DriverManager.getDrivers();
    while (drivers.hasMoreElements()) {
      Driver driver = drivers.nextElement();
      try {
        DriverManager.deregisterDriver(driver);
      } catch (SQLException e) {
        message += "ERROR(contextDestroyed): " + Utils.getStackTrace(e);
      }
    }
  }

  JdbcTemplate jdbcTemplate;

  public Db() {
    jdbcTemplate = new JdbcTemplate(sqlServerDataSource());
  }

  @Bean(destroyMethod = "shutdownNow")
  private DataSource sqlServerDataSource() {
    DataSourceBuilder dataSourceBuilder = DataSourceBuilder.create();
    dataSourceBuilder.url(
      "jdbc:sqlserver://sbserver.net;databaseName=gg"
    );
    dataSourceBuilder.username("user");
    dataSourceBuilder.password("pass");
    return dataSourceBuilder.build();
  }

  public String runSome() {
    message = "From runSome: \n\n";

    try {
      message += "trying to execute jdbcTemplate...\n";
      String sql = "select * from gg.objecttypes";

      List<ObjectType> objectTypes = jdbcTemplate.query(
        sql,
        new BeanPropertyRowMapper(ObjectType.class)
      );

      message += "\n\n";
      for (final ObjectType objectType : objectTypes) {
        message +=
          objectType.getId() + " :: " + objectType.getObjecttype() + "\n";
      }

      message +=
        "::executed jdbcTemplate! " + objectTypes.size() + "\n";
    } catch (Exception e) {
      message += "ERROR(runSome): " + Utils.getStackTrace(e);
    }

    return message;
  }
}

我甚至不确定contextinitialized和contextdestroyed方法是否正在执行。
然后我可以从我的控制器调用该方法:

@GetMapping(value = "/admin/user-health")
  public String userHealth() {

    String message = "From userHealth: \n\n";
    try {
        message += "trying to execute db.runSome()...\n";
        Db db = new Db();
        message += db.runSome();
        message += "executed db.runSome()!\n";
    } catch (Exception e) {
        message += "ERROR(userHealth): " + Utils.getStackTrace(e);
    }
    return message;

  }

同样,它工作正常,我从数据库得到结果。虽然最终我得到了一个连接超时。
问题是内存泄漏,因为线程永远不会死,我认为我没有用户。
我怎样才能解决这个问题?

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题