前言
tomcat 进程突然关闭(未完成)
监控软件跟踪到 日志显示 如下
{
"appName": "xxx",
"blockedCount": 8656,
"blockedTime": -1,
"hostname": "xx",
"inNative": false,
"lockOwnerId": 0,
"stackTrace": "sun.misc.Unsafe.park(Native Method)
java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:215)
java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2078)
java.util.concurrent.LinkedBlockingQueue.poll(LinkedBlockingQueue.java:467)
org.springframework.amqp.rabbit.listener.BlockingQueueConsumer.nextMessage(BlockingQueueConsumer.java:188)
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.doReceiveAndExecute(SimpleMessageListenerContainer.java:466)
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.receiveAndExecute(SimpleMessageListenerContainer.java:455)
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer.access$300(SimpleMessageListenerContainer.java:58)
org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer$AsyncMessageProcessingConsumer.run(SimpleMessageListenerContainer.java:548)
java.lang.Thread.run(Thread.java:745)
",
"suspended": false,
"threadID": 419,
"threadName": "SimpleAsyncTaskExecutor-171",
"threadState": "TIMED_WAITING",
"timestamp": 1526904360000,
"waitedCount": 3685416,
"waitedTime": -1
}
看懂这个公式 表示的问题,有助于了解问题,比如waitedCount 的含义等
java 进程挂掉的几个分析方向
- 在jvm 脚本中 增加 -XX:+HeapDumpOnOutOfMemoryError 看看进程退出时的堆内存数据
- 被Linux OOM killer 干掉。该killer 会在内存不足的时候 kill 掉任何不受保护的进程,从而释放内存,保证kernel 的运行。若进程 是被 killer 干掉的,则
/var/log/messages
会有相关的日志,比如Out of memory:Kill process 31201(java) score 783 or sacrifice child
,也可以使用dmesg | egrep -i ‘killed process’
此时通常有两个办法:将自己的进程设置为受保护;找一个内存富余点的服务器。==> 分析下挂掉的几次 是否是同一台服务器。
销毁问题
代码背景
public class A {
public static B b = null;
static {
b = init();
}
public static void opt(){
try{
b.opt();
if(condition){
return;
}else{
opt();
}
}catch(Exception e){
print(e)
}finally{
destroy(b)
}
}
}
问题1
从代码上看,b只初始化了一次,那么当A.opt()
执行出错后,再调用A.opt()
时,b.opt()
将无法正常执行。
解决问题1
public class A {
public static B b = null;
public static void opt(){
b = init();
try{
b.opt();
if(condition){
return;
}else{
opt();
}
}catch(Exception e){
print(e)
}finally{
destroy(b)
}
}
}
问题2
将b的初始化代码移到opt方法后,可以解决问题1。但因为opt可能会产生递归操作,如果要求递归过程中保持同一个b引用, 则该方案无法解决。
解决问题2
既然问题出在b的初始化代码,要确保每次执行时,b都可用,又要递归时,保持同一个引用,则可以将初始化代码和递归代码分开。
public class A {
public static void opt(){
B b = init();
try{
doOpt(b);
}catch(Exception e){
print(e)
}finally{
destroy(b)
}
}
private static void doOpt(B b) throws Exception{
b.opt();
if(condition){
return;
}else{
doOpt(b);
}
}
}
有时候,能将问题描述清楚,就离解决问题不远了。
对象的的初始化与销毁
有些时候,b有自己的管理组件,比如缓存、或线程池。此时,b的获取和回收就跟常规的方式不同(甚至不用回收)。如果不考虑这些问题,直接使用b.close()
之类的方法,或许会对其他组件的使用带来负面影响。