【问题标题】:Tomcat doesn't stop. How can I debug this?Tomcat 不会停止。我该如何调试呢?
【发布时间】:2012-04-15 20:16:15
【问题描述】:

我有一个在 Linux 中运行的Tomcat 7,我通过$CATALINA_HOME/bin/startup.sh 启动并通过$CATALINA_HOME/bin/shutdown.sh 关闭
来自/etc/init.d

除 1 个问题外,一切正常。有时tomcat不会停止。
虽然我停止了它并且我在 catalina.out 日志中看到它正在下降,但如果我这样做 ps -ef 我仍然可以看到进程正在运行。

可能是什么问题?我该如何调试呢?我的感觉是,这与线程有关。

所以可疑的部分如下:
1) 我使用 Log4j 的 LogManager 来检测 log4j 配置是否已更改,但我在 contextDestroyed ServletContextListener 上执行 Log4jManager.shutdown
2)我使用H2数据库,我在关机时看到:

严重:Web 应用程序 [/MyApplication] 似乎已启动一个
名为 [H2 Log Writer MYAPPLICATION] 的线程,但未能阻止它。
这很可能会造成内存泄漏

严重:Web 应用程序 [/MyApplication] 似乎已启动一个
线程名为 [H2 File Lock Watchdog
/opt/myOrg/tomcat/webapps/MyApplication/db/myDatabase.lock.db] 但有
未能阻止它。这很可能造成内存泄漏。 4 月 2 日,
2012 上午 9:08:08 org.apache.catalina.loader.WebappClassLoader
clearReferencesThreads 严重:Web 应用程序 [/MyApplication]
似乎已经启动了一个名为 [FileWatchdog] 的线程,但失败了
阻止它。这很可能造成内存泄漏。

有什么帮助吗?我怎样才能发现这里的问题?

更新:
我按照@daveb 的建议做了一个kill -3,在catalina.out 中我看到了:

JVMDUMP006I 正在处理转储事件“用户”,详细信息“” - 请稍候。 JVMDUMP032I JVM 请求 Java 转储,使用 '/etc/init.d/javacore.20120402.093922.2568.0001.txt' 响应 事件 JVMDUMP010I Java 转储写入 /etc/init.d/javacore.20120402.093922.2568.0001.txt JVMDUMP013I 已处理转储事件“用户”,详细信息“”。

/etc/init.d 中有一个 javacore,但我不知道如何处理它。 IE。我应该调查哪些部分

【问题讨论】:

  • 尝试将您的线程标记为守护线程,这样虚拟机就不会等待它们死亡。 docs.oracle.com/javase/6/docs/api/java/lang/… 当然,这只对你自己的线程有用,对H2启动的线程没有用
  • H2 只创建守护线程。
  • @ThomasMueller:那么 SEVERE: The web application [/MYAPPLICATION] appears to have started a thread named [H2 File Lock Watchdog /opt/myOrg/tomcat/webapps/MyApplication/lock.db] but has failed to stop it. This is very likely to create a memory leak. 在 catalina.out 中是什么意思?我没有创建这个,而是 H2
  • @ThomasMuller:我在您的 Google 群组中添加了一个帖子。它说它正在审核中。由于我的错误,我没有添加与 Tomcat 开发人员讨论的链接:mail-archives.apache.org/mod_mbox/tomcat-users/201204.mbox/…不过我会更新的

标签: java linux multithreading tomcat h2


【解决方案1】:

通过使用 jstack 或向进程发送信号,找出哪些线程仍在运行(或阻塞、等待运行):

kill -3 pid

当您知道这一点时,您可以将启动它们的任何东西挂接到关闭通知中以停止线程。或者让这些线程成为守护线程。

请参阅This tomcat shutdown question 了解更多详情。

如果您不知道线程是在哪里创建的,请考虑为它们添加名称 - 执行程序可以采用线程工厂,您可以使用这些工厂来设置线程的守护进程状态并为其命名 - 这样您的堆栈跟踪会更清晰。

【讨论】:

  • 在文本编辑器中打开 javacore .txt 文件,查找包含您编写的包的线程堆栈。
  • 是的,我猜到了。但我不确定什么表明错误。
【解决方案2】:

检查您的 Web 应用程序是否有一些调度程序处于活动状态,例如 Quartz。

如果你不停止它,Web 应用程序线程将永远不会结束,直到你杀死它

【讨论】:

    【解决方案3】:

    我遇到了完全相同的问题。有时,命令./shutdown.sh 并没有停止tomcat 进程,它的java 进程停留在正在运行的进程中。

    我已经使用 Ubuntu 软件存储库中的 Tomcat 版本解决了这个问题,作者:

    sudo apt-get install tomcat7
    

    从包管理器安装它并配置一些设置后,我在停止/启动 Tomcat 时没有任何问题。我用这个命令停止,它从来没有失败过:

    service tomcat7 stop
    

    几乎和

    一样
    /etc/init.d/tomcat7 stop
    

    使用此命令运行 init 脚本中的代码块,特别是文件 /etc/init.d/tomcat7 中的代码。所以我研究了它,看看它总是成功地杀死tomcat进程。以下是使用service tomcat7 stop 命令时运行的代码块:

    log_daemon_msg "Stopping $DESC" "$NAME"
    
            set +e
            if [ -f "$CATALINA_PID" ]; then
                    start-stop-daemon --stop --pidfile "$CATALINA_PID" \
                            --user "$TOMCAT7_USER" \
                            --retry=TERM/20/KILL/5 >/dev/null
                    if [ $? -eq 1 ]; then
                            log_progress_msg "$DESC is not running but pid file exists, cleaning up"
                    elif [ $? -eq 3 ]; then
                            PID="`cat $CATALINA_PID`"
                            log_failure_msg "Failed to stop $NAME (pid $PID)"
                            exit 1
                    fi
                    rm -f "$CATALINA_PID"
                    rm -rf "$JVM_TMP"
            else
                    log_progress_msg "(not running)"
            fi
            log_end_msg 0
            set -e
            ;;
    

    重要的是:

    start-stop-daemon --stop --pidfile "$CATALINA_PID" \
                                --user "$TOMCAT7_USER" \
                                --retry=TERM/20/KILL/5 >/dev/null
    

    这意味着“重试停止,直到进程停止。这是 start-stop-daemon 手册中的 --retry 命令文档:

       -R|--retry timeout|schedule
              With  --stop,  specifies  that  start-stop-daemon  is  to  check
              whether  the  process(es)  do  finish.  It will check repeatedly
              whether any matching processes are running, until none are.   If
              the  processes  do  not exit it will then take further action as
              determined by the schedule.
    
              If timeout is specified instead of schedule  then  the  schedule
              signal/timeout/KILL/timeout  is used, where signal is the signal
              specified with --signal.
              ...
    

    所以,--retry=TERM/20/KILL/5 表示“向进程发送 TERM 信号,等待 20 秒,如果它仍在运行,则发送 KILL 信号,等待 5 秒,如果是还在运行,有问题。

    这意味着您可以将 tomcat 配置为作为守护程序运行并使用这样的命令,或者编写脚本来执行此类操作以停止 tomcat,或者只使用 Ubuntu 并从包管理器中获取 tomcat。

    【讨论】:

      【解决方案4】:

      如果 Web 应用程序停止,与数据库的所有连接也应关闭。如果您没有连接列表,则执行 SQL 语句“shutdown”(这只适用于 H2 和 HSQLDB 数据库)。

      如果您注册了一个 Servlet,您可以在 Servlet.destroy() 方法中执行此操作。

      如果你已经注册了ServletContextListener,你可以在ServletContextListener.contextDestroyed(ServletContextEvent servletContextEvent)方法中执行“shutdown”语句。这就是 org.h2.server.web.DbStarter ServletContextListener 所做的(包含在 H2 数据库中的那个)。

      【讨论】:

      • 感谢您的回复!If the web application is stopped, all connections to the database should be closed as well。我正在使用 Tomcat 的连接池。所以它不在我的掌控之中。这是 Tomcat 和 H2 之间已知的“赛车”问题吗?我可以做SHUTDOWN(它是安全的,对吧?)但我想确保我正确地解决了这个问题
      • 它看到了问题。不确定最好的解决方案是什么...忽略异常?语句SHUTDOWN 只会关闭这个数据库,所以应该比较省——只有当你不确定其他web 应用程序是否在使用这个数据库时,你不能这样做。另一种解决方案是使用服务器模式(在另一个进程中运行H2数据库)。
      • :没有数据库是我的,即没有其他应用程序可以访问它。我需要在文件模式下使用它。所以基本上你是说 1)关闭对数据是安全的 2)我可以忽略异常是什么意思?Tomcat不会因为H2仍在运行而在关机时挂起?我在我的应用程序目录中看到db.lock文件
      • 只要数据库处于打开状态,<databaseName>.lock.db 文件就不会被删除。数据库已打开,因为至少有一个打开的连接。至少有一个打开的连接,因为连接池没有被释放。如果您执行shutdown,则数据库将关闭。在你的情况下,那就是保存。
      • 在我的情况下,当 tomact 停止时,我必须停止所有连接,包括石英线程。即使我有 ServletContextListener.contextDestroyed 方法,但我不知道如何在 tomcat 停止时停止所有连接
      【解决方案5】:

      我也遇到了同样的问题。我的应用程序中有一个 ThrottledThreadPoolExecutor 没有关闭。当我正确关闭它时,tomcat会干净地停止。为了找出问题所在,我不得不从我的tomcat webapps 目录中删除所有应用程序,然后逐个添加它们,看看是哪一个导致了问题

      【讨论】:

        【解决方案6】:

        如果您在 Web 应用程序中使用调度程序或其他实体,则需要将其关闭。通常,您必须使用 ServletContextListener 来提供挂钩以进行关闭调用。在这种情况下,关闭挂钩将不起作用,因为 JVM 还没有关闭。相信我,我已经试过了。如果您的代码位于代理代码或容器/webapp 之外的其他内容中,则关闭挂钩应该可以工作,尽管通常要弄清楚为什么它仍然无法正常工作是一件很费劲的事情。注意,我是秃头。

        【讨论】:

        • 还有这个有用的线程:serverfault.com/questions/1021145/…。哦,天哪,我很确定我没有产生我的线程 - 但我真的做到了,我只是忘记了。我非常确定我正在搜索我们正在使用的遗留 iBatis 的旧错误报告,认为这是罪魁祸首 - 没有清理连接等。我错了吗。
        【解决方案7】:

        在我的情况下,我有一个流氓 JPA EntityManager 在使用后没有正确关闭。修复了这个问题,现在我可以再次清理并构建,而无需每次都杀死该死的 Java 进程:)

        【讨论】:

          猜你喜欢
          • 2012-05-07
          • 1970-01-01
          • 1970-01-01
          • 2011-03-15
          • 2012-01-07
          • 2012-04-29
          • 1970-01-01
          • 2016-08-16
          • 1970-01-01
          相关资源
          最近更新 更多