问题的现象:
因为个人对tomcat不熟悉,所以网上找了下tomcat假死的原因,大致有如下几种:
与tomcat连接未关闭/长连接数超过最大连接数
- Load过高,超出服务器极限
- 应用程序出现死锁
- JVM GC时间过长,导致应用暂停/JVM内存溢出
按顺序一个一个来排查:
- top一下, load average不高,说明不是服务器负载太高了
- 看下是否是死锁的问题
然后使用vim找了下“DEADLOCK”,没有找到,应该是没有出现死锁(最好dump多次,才能更好的定位问题)
- 使用visualvm查看下tomcat进程的堆栈情况。(怎么使用visualvm看我之前写的那篇文章-“使用visualvm远程监控Java程序”),发现堆内存占用很大。一段时间后,日志里面也报了错误:
下面的图是我在出现内存溢出后截的:
那么接下来就是分析是哪里导致内存溢出了:
先把heap信息导出来:
使用mat工具分析:(mat网上随便下吧,我想传上去骗点分,老是显示资源已经有了…)
./ParseHeapDump.sh ../tomcat.bin org.eclipse.mat.api:suspects org.eclipse.mat.api:overview org.eclipse.mat.api:top_components
会出来这么三个文件,里面都是html网页,记录了堆信息。
主要是看TopConsumer,这里记录了内存消耗大户:
这里判断那里内存溢出,需要结合代码以及堆栈文件了。结合工程的逻辑,这里一看就知道是多线程的问题,线程太多并且每个线程要拉取的HBase数据太多造成JVM内存溢出。
dump文件分析介绍
虽然dump文件中没有死锁,但是看懂dump文件对于了解程序运行状态还是有帮助的,所以这里介绍下dump文件中的内容。如果有说的不对的地方,麻烦大家及时指正下呀!
如果问题不好定位的话,需要dump多次,每次间隔个几秒钟,然后结合着一起看。
Dump文件结构大致如下:
JAVA EE部分。(具体信息详见“参考3”,忽略上面的weblogic.Server.main部分,那是参考文章作者自己的内容)。
来看下JAVA EE这部分的内容,从堆栈文件中可以看到线程会显示两种状态,一种是操作系统线程状态,一种是JVM线程状态(在Thread.State枚举类中有说明):
JVM定义的六种状态中来(在java.lang.Thread.State类中)。接下来分别细说下JVM线程状态。
JVM线程运行状态:
NEW:
线程刚创建且未启动
RUNNABLE:
线程处于运行状态
BLOCKED:
线程等待获取监视器锁(monitor lock)以期进入同步代码块/方法(即等待锁,并且是synchronized)中。(下面会说到Monitor中的Entry Set和Wait Set,BLOCKED还有下面即将说到的WAITING和TIMED_WAITING都和这两个队列有关)
WAITING:
线程无期限的等待另外一个线程来执行一个特定的操作,等待另外一个线程调用notify()/notifyAll()。特定操作可以是:Object.wait()、Thread.join()、LockSupport.park()
TIMED_WAITING:
线程在一段时间范围内等待另外一个线程来执行特定操作,时间到了之后又回到运行状态。特定操作可以是:Object.wait(time)、Thread.join(time)、LockSupport.park(time)、Thread.sleep(time)
Ps1: sleep可以在任何地方使用, wait、notify、notifyAll只能在同步控制方法或同步控制块中使用;sleep不会释放锁,但是wait会;结合以上两点,WAITING状态结束之后会转为BLOCKED状态,因为它是要重新取获取锁的。
Ps2:ReentrantLcok中未获取到锁时调用的是park/parkNanos/(进入定义的队列),所以用ReentrantLock是进入到WAITING/TIMED_WAITING状态。
TERMINATED:
线程运行结束
Entry Set & Wait Set介绍:

每个Java对象有且仅有一个Moniter,Entry Set和Wait Set属于这个Moniter的两个队列,又可以称为锁池和等待队列,他们是Java中用以实现线程之间的互斥与协作的主要手段,可以看成是对象或者class的锁。等待队列中的线程不能去争取锁,只有晋升到锁池队列中的线程才能去争取锁,这个就是两者的区别。如果对象调用的是notify,那么只有一个线程能进入到锁池队列,如果调用的是notifyAll,所有线程才能全部进到锁池队列中一起争夺锁。
实际一些日志中的信息分析:
BLOCKED状态:
正在等待获取锁0x0000***188
CPU占用高,load高,响应很慢:
CPU占用不高,响应慢:
BLOCKED状态的越来越多,很有可能被一个大锁锁住了
WAIT状态越来越多,那么可能是线程停在了I/O,数据库连接或者网络连接等地方
或者直接就是两者很多
死锁:

参考:
https://www.javatang.com/archives/2017/10/25/36441958.html(Dump文件如何分析)
https://www.jianshu.com/p/8fe0b117cacd(Java线程状态和系统线程状态区别)
https://www.oschina.net/translate/jvm-how-to-analyze-thread-dump?print(dump文件结构图)
https://www.javatang.com/archives/2017/10/26/08572060.html(dump文件一些典型案例)