Tomcat не останавливается. Как я могу это отладить?

У меня Tomcat 7 работает в Linux, который я запускаю через $CATALINA_HOME/bin/startup.sh и выключаю через $CATALINA_HOME/bin/shutdown.sh
из /etc/init.d

Все в порядке, кроме одной проблемы. Иногда tomcat не останавливается.
Хотя я останавливаю его и вижу в журналах catalina.out, что он не работает, если я это сделаю ps -ef, я все равно смогу увидеть, что процесс запущен.

В чем может быть проблема? Как я могу это отладить? Мне кажется, что это связано с потоками.

Таким образом, подозрительными являются следующие части:
1) Я использую LogManager Log4j, чтобы определить, была ли изменена конфигурация log4j, но я делаю Log4jManager.shutdown на contextDestroyed ServletContextListener
2) Я использую базу данных H2 и вижу неисправность:

СЕРЬЕЗНО: веб-приложение [/ MyApplication], похоже, запустило
поток с именем [H2 Log Writer MYAPPLICATION], но не смогло его остановить.
Очень вероятно, что это приведет к утечке памяти

СЕРЬЕЗНО: веб-приложение [/ MyApplication], похоже, запустило поток
под названием [H2 File Lock Watchdog
/opt/myOrg/tomcat/webapps/MyApplication/db/myDatabase.lock.db], но имеет
Не удалось это остановить. Это может привести к утечке памяти. 2 апреля,
2012 9:08:08 AM org.apache.catalina.loader.WebappClassLoader
clearReferencesThreads СЕРЬЕЗНО: веб-приложение [/ MyApplication], похоже, запустило поток с именем [FileWatchdog], но имеет не удалось
остановить это. Это может привести к утечке памяти.

Любая помощь, пожалуйста? Как я могу обнаружить здесь проблему?

ОБНОВЛЕНИЕ:
Я выполнил kill -3, как предложил @daveb, и в 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 Processed dump "пользователь", деталь "".

В /etc/init.d есть javacore, но я не знаю, как его обработать. Т.е. какие части я должен исследовать


person Jim    schedule 02.04.2012    source источник
comment
Попробуйте пометить свои потоки как потоки демона, чтобы виртуальная машина не дождалась их смерти. docs.oracle.com/javase / 6 / docs / api / java / lang / Но, конечно, это полезно только для ваших собственных потоков, а не для тех, которые были запущены H2.   -  person luukes    schedule 02.04.2012
comment
H2 создает только потоки демонов.   -  person Thomas Mueller    schedule 02.04.2012
comment
@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.   -  person Jim    schedule 02.04.2012
comment
Это все еще поток демона. Тот факт, что поток не был остановлен, означает, что база данных еще не была закрыта. База данных закрывается, если все подключения к базе данных закрыты или если вы выполняете оператор shutdown.   -  person Thomas Mueller    schedule 02.04.2012
comment
@ThomasMueller: Но мои соединения из пула соединений Tomcat. Таким образом, Tomcat отвечает за их закрытие. Я всегда закрываю их, но Tomcat помещает их, но в пул. Это известная проблема? Как я могу решить эту проблему? Должен ли я делать выключить сам? У меня также есть этот пост на stackoverflow.com/questions/9972372/   -  person Jim    schedule 02.04.2012
comment
Это похоже на то, что Tomcat не удаляет пул соединений ... Я думаю, что Tomcat должен сначала удалить пул соединений, а затем потоки журнала, которые все еще работают. Так что, на мой взгляд, это проблема, которую можно исправить в Tomcat, но не в H2.   -  person Thomas Mueller    schedule 02.04.2012
comment
@ThomasMueller: Но если это проблема Tomcat, почему эта проблема возникает только с H2?   -  person Jim    schedule 02.04.2012
comment
С какими еще базами данных вы тестировали?   -  person Thomas Mueller    schedule 02.04.2012
comment
Я использовал MySQL и MS-SQL в прошлом и никогда не имел таких тонкостей. Я не пытаюсь винить H2. Я просто пытаюсь понять, как это обойти.   -  person Jim    schedule 03.04.2012
comment
@ThomasMueller: У меня есть контакт с парнями из Tomcat по этой проблеме, и они говорят, что проблема в том, что драйвер H2 использует загрузчик классов веб-приложения в качестве загрузчика классов контекста, когда у вас должен быть загрузчик классов, используемый для загрузки JDBC. Это создает драйвер утечки памяти   -  person Jim    schedule 05.04.2012
comment
@Jim Спасибо! Я еще не совсем понимаю все это, но я разберусь с этим.   -  person Thomas Mueller    schedule 05.04.2012
comment
@ThomasMueller: Если вы хотите, я могу отправить вам ссылку по электронной почте на обсуждение с разработчиком Tomcat. Я не уверен, где вы принимаете проблемы с H2 на своем сайте H2   -  person Jim    schedule 05.04.2012
comment
@Jim, вы можете отправить письмо в группу H2 Google, создать проблему в Google Code (проект h2database) или сообщить мне здесь - все в порядке. Было бы неплохо дать ссылку на обсуждение по почте!   -  person Thomas Mueller    schedule 05.04.2012
comment
@ThomasMuller: Я добавил сообщение в вашу группу Google. В нем говорится, что оно находится на модерации. По моей ошибке я не добавил ссылку на обсуждение с Tomcat dev, которое находится здесь: mail-archives.apache.org/mod_mbox/tomcat-users/201204. mbox / Я все же обновлю   -  person Jim    schedule 10.04.2012
comment
@ThomasMueller: Здесь также mail-archives.apache.org/mod_mbox/tomcat-users/201204.mbox/   -  person Jim    schedule 10.04.2012
comment
@ThomasMueller: Я отправил вам образец, воспроизводящий проблему   -  person Jim    schedule 18.04.2012
comment
jstack и jvisualvm - полезные инструменты для диагностики таких проблем.   -  person cambunctious    schedule 10.10.2018


Ответы (7)


Если веб-приложение остановлено, все подключения к базе данных также должны быть закрыты. Если у вас нет списка подключений, выполните оператор SQL «shutdown» (это работает только для баз данных H2 и HSQLDB).

Если у вас есть зарегистрированный сервлет, вы можете сделать это с помощью метода Servlet.destroy().

Если вы зарегистрировали ServletContextListener, вы можете выполнить оператор "shutdown" в методе ServletContextListener.contextDestroyed(ServletContextEvent servletContextEvent). Это то, что делает org.h2.server.web.DbStarter ServletContextListener (тот, который включен в базу данных H2).

person Thomas Mueller    schedule 02.04.2012
comment
Спасибо за ответ! If the web application is stopped, all connections to the database should be closed as well. Я использую пул соединений Tomcat. Так что это не в моих руках. Это известная проблема "гонок" между Tomcat и H2. Я могу сделать SHUTDOWN (это безопасно, правда?), Но я хочу убедиться, что решаю ее правильно - person Jim; 02.04.2012
comment
В этом проблема. Не уверены, какое решение лучше ... игнорировать исключение? Оператор SHUTDOWN только закроет эту базу данных, поэтому он должен быть относительно экономным - только если вы не уверены, что другие веб-приложения используют эту базу данных, вы не можете этого сделать. Другое решение - использовать режим сервера (запустить базу данных H2 в другом процессе). - person Thomas Mueller; 02.04.2012
comment
: Нет, это моя база данных, т.е. никакие другие приложения не будут к ней обращаться. И я должен использовать ее в файловом режиме. Итак, в основном вы говорите, что 1) завершение работы безопасно для данных 2) Я могу игнорировать исключение, что означает? Tomcat не зависает при завершении работы из-за того, что H2 все еще работает? Я вижу файл db.lock в каталоге моего приложения - person Jim; 02.04.2012
comment
Файл <databaseName>.lock.db не удаляется, пока открыта база данных. База данных открыта, потому что есть хотя бы одно открытое соединение. Есть хотя бы одно открытое соединение, потому что пул соединений не удален. Если вы выполните shutdown, то база данных будет закрыта. В вашем случае это экономия. - person Thomas Mueller; 02.04.2012
comment
В моем случае мне нужно остановить все соединения, включая кварцевый поток, когда tomact останавливается. Даже у меня есть метод ServletContextListener.contextDestroyed, но я не знаю, как остановить все соединения, когда tomcat останавливается - person Vicky; 27.08.2016

Узнайте, какие потоки все еще работают (или заблокированы, ожидают запуска), используя jstack или отправив сигнал процессу:

kill -3 pid

Когда вы это знаете, вы можете сделать так, чтобы они подключились к уведомлению о завершении работы, чтобы остановить потоки. Или сделайте эти нити деамонными нитями.

См. этот вопрос о завершении работы tomcat для подробнее об этом.

Если вы не знаете, где были созданы ваши потоки, подумайте о добавлении к ним имен - исполнители могут использовать фабрики потоков, и вы можете использовать эти фабрики для установки статуса deamon потока, а также для присвоения ему имени - так что трассировка стека будет будьте яснее.

person daveb    schedule 02.04.2012
comment
Откройте файл javacore .txt в текстовом редакторе, найдите стеки потоков, которые включают пакеты, которые вы написали. - person daveb; 02.04.2012
comment
Да, я это понял, но я не уверен, что указывает на ошибку. - person Jim; 02.04.2012

Убедитесь, что в вашем веб-приложении активен какой-нибудь планировщик, например Quartz.

Если вы его не остановите, поток веб-приложений никогда не закончится, пока вы его не убьете.

person Marco    schedule 08.09.2014

У меня была точно такая же проблема. Иногда команда ./shutdown.sh не останавливает процесс tomcat, и его java процесс остается в запущенных процессах.

Я решил эту проблему, используя версию Tomcat в репозиториях программного обеспечения Ubuntu:

sudo apt-get install tomcat7

После установки из диспетчера пакетов и настройки некоторых параметров у меня не возникло проблем с остановкой / запуском Tomcat. Я использовал эту команду, чтобы остановиться, и она никогда не подводила:

service tomcat7 stop

что почти то же самое, что и

/etc/init.d/tomcat7 stop

Использование этой команды запускает блок кода из сценария инициализации, в частности, коды из файла /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

Это означает «повторять остановку, пока процесс не будет остановлен. Вот документация по команде --retry из руководства start-stop-daemon:

   -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 для запуска как deamon и использовать такую ​​команду, или написать сценарий для выполнения такого действия, чтобы остановить tomcat, или просто использовать Ubuntu и получить tomcat из диспетчера пакетов.

person Utku Özdemir    schedule 17.02.2014

У меня тоже была такая же проблема. В моем приложении был ThrottledThreadPoolExecutor, который не завершал работу. Когда я выключил его должным образом, кот остановился чисто. Чтобы выяснить проблему, мне пришлось удалить все приложения из моего каталога tomcat webapps, а затем добавить их одно за другим и посмотреть, какое из них вызывает проблему.

person Denorm    schedule 08.12.2015

Если вы используете планировщик или какой-либо другой объект в своем веб-приложении, вам необходимо закрыть его. Обычно вам нужно использовать ServletContextListener, чтобы предоставить ловушку для вызова завершения работы. В этом случае ловушка выключения не сработает, потому что JVM не завершает работу (пока). Поверьте, я пробовал. Если ваш код находится в коде агента или в чем-то за пределами контейнера / веб-приложения, тогда ДОЛЖНА работать ловушка выключения, хотя часто бывает сложно понять, почему она ВСЕ ЕЩЕ не работает. Заметьте, я лысый.

person ticktock    schedule 02.08.2016
comment
также есть эта полезная ветка: serverfault.com/questions/1021145/. и о боже, я был так уверен, что не порожу своих нитей - и все же я действительно сделал, я просто забыл. Я был так уверен, что искал старые отчеты об ошибках в устаревших версиях iBatis, которые мы используем, думая, что это было виновато, а не очистка соединений и тому подобное. я был так неправ. - person hello_earth; 04.01.2021

В моем случае у меня был один мошеннический JPA EntityManager, который не закрывался должным образом после использования. Исправлено это, и теперь я могу снова выполнить Clean-and-Build, не убивая каждый раз этот чертов Java-процесс :)

person JohannSig    schedule 30.12.2014