执行了tomcat的shutdown脚本后,java进程仍然存在,认为tomcat的关闭脚本不可靠。好吧,其实shutdown.sh无法停止,不是tomcat问题,是应用有问题,否则可以跑一个空tomcat,保证shutdown百分之百生效。
先说一下tomcat的大概关闭过程:
1 停止连接处理线程Accepter,停止接受新的请求
2 关闭tomcat自身的资源,例如各种service,连接器,protocol,container
3 然后tomcat主线程结束了,就是执行BootStrap这个类的线程
这是一个平滑关闭的过程,但是什么时候会导致所谓”tomcat无法关闭”?要更正一点,不是tomcat无法关闭,是执行tomcat的这个jvm无法关闭,这是本质的不同。
最根本的原因是:当前运行tomcat的jvm里还有非deamon的线程没有结束执行,例如被阻塞,或者还在执行任务。这个现象就是tomcat 端口都已经关闭了,但是java进程还在。tomcat的停止只是结束了自己的线程,关闭了自己的资源。但是应用开启的非deamon线程,这个 tomcat是无能为力的。
netstat -ano|grep 8083 /*可以通过此确认tomcat是否已关闭*/
那么JVM什么时候停止?没有非deamon的线程在运行就停止了,或者说应用自身的非deamon线程处理完所有事情结束了自己,jvm就停止了。
那么当发现tomcat停止了,但是ps -ef|grep tomcat进程依然在,如何查看应用那个地方线程是非deamon的呢?
查看办法很简单,执行如下命令:
$JAVA_HOME/bin/jstack <pid>
例:
[root@localhost bin]# ps -ef|grep tomcat
root 1513 1 2 23:41 pts/1 00:00:01 /usr/local/share/java/jdk1.6.0_25/bin/java -Djava.util.logging.config.file=/opt/apache-tomcat-6.0.32/conf/logging.properties -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager -Djava.endorsed.dirs=/opt/apache-tomcat-6.0.32/endorsed -classpath /opt/apache-tomcat-6.0.32/bin/bootstrap.jar -Dcatalina.base=/opt/apache-tomcat-6.0.32 -Dcatalina.home=/opt/apache-tomcat-6.0.32 -Djava.io.tmpdir=/opt/apache-tomcat-6.0.32/temp org.apache.catalina.startup.Bootstrap start
root 1544 1462 0 23:42 pts/1 00:00:00 grep --color=auto tomcat
可以看到tomcat进展pid为1513,则下面定位到jdk/bin目录,执行jstack1513 查看
工程里到底哪里开启的新的非守护线程?
$ cd /home/software/jdk1.7.0_79/bin
$ jstack 1513
调用jstack查看:
[root@localhost bin]# jstack 1513
2011-07-12 23:44:00
Full thread dump Java HotSpot(TM) Client VM (20.0-b11 mixed mode, sharing):
"Attach Listener" daemon prio=10 tid=0xb41d7c00 nid=0x606 waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE
"TP-Monitor" daemon prio=10 tid=0xb41d6400 nid=0x5fa in Object.wait() [0xb3e0b000]
java.lang.Thread.State: TIMED_WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x87143720> (a org.apache.tomcat.util.threads.ThreadPool$MonitorRunnable)
at org.apache.tomcat.util.threads.ThreadPool$MonitorRunnable.run(ThreadPool.java:565)
- locked <0x87143720> (a org.apache.tomcat.util.threads.ThreadPool$MonitorRunnable)
at java.lang.Thread.run(Thread.java:662)
"TP-Processor4" daemon prio=10 tid=0xb41d4c00 nid=0x5f9 runnable [0xb3e5c000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
- locked <0x87124770> (a java.net.SocksSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:462)
at java.net.ServerSocket.accept(ServerSocket.java:430)
at org.apache.jk.common.ChannelSocket.accept(ChannelSocket.java:311)
at org.apache.jk.common.ChannelSocket.acceptConnections(ChannelSocket.java:668)
at org.apache.jk.common.ChannelSocket$SocketAcceptor.runIt(ChannelSocket.java:879)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:690)
at java.lang.Thread.run(Thread.java:662)
其中看到"Attach Listener" daemon prio=10 tid=0xb41d7c00 nid=0x606 waiting on condition [0x00000000]
java.lang.Thread.State: RUNNABLE
其中主线程不是daemon的,所以是这样:
"main" prio=10 tid=0xb6d05000 nid=0x5ea runnable [0xb6ee9000]
java.lang.Thread.State: RUNNABLE
at java.net.PlainSocketImpl.socketAccept(Native Method)
at java.net.PlainSocketImpl.accept(PlainSocketImpl.java:408)
- locked <0x871644a8> (a java.net.SocksSocketImpl)
at java.net.ServerSocket.implAccept(ServerSocket.java:462)
at java.net.ServerSocket.accept(ServerSocket.java:430)
at org.apache.catalina.core.StandardServer.await(StandardServer.java:431)
at org.apache.catalina.startup.Catalina.await(Catalina.java:676)
at org.apache.catalina.startup.Catalina.start(Catalina.java:628)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at org.apache.catalina.startup.Bootstrap.start(Bootstrap.java:289)
at org.apache.catalina.startup.Bootstrap.main(Bootstrap.java:414)
在"main" 后没有daemon,看到这样的线程状态,顺藤摸瓜,找到对应new Thread的地方setDaemon(true)就可以痛痛快快的关闭tomcat,停止应用了。