最近服务器发现tomcat的应用会偶尔出现没法访问的状况。通过一段时间的观察最近又发现有台tomcat的应用出现了没法访问状况。
简单描述下该台tomcat当时具体的表现:客户端请求没有响应,查看服务器端tomcat的进程是存活的,查看业务日志的时候发现日志中止没有任何最新的访问日志。
连tomcat下面的catalina.log也没有任何访问记录,基本判定该台tomcat已不能提供服务。
根据前面我描述的假死现象,我最早想到的是网络是否出现了问题,是否是有什么丢包严重的状况,因而我开始从请求的数据流程开始分析,因为咱们业务的架构采用的是nginx+tomcat的集群配置,一个请求上来的流向能够用下图来简单的描述一下:linux
更改nginx的配置,让该台nginx请求只转到本机器的出现问题的tomcat应用上面,在access.log里看是否有网络请求,结果能够查看到当前全部的网络请求,也就是说能够排除是网络的问题。
分析业务配置的tomcat访问日志xxxx.log上是否有日志访问记录,通过查询该台tomcat应用日志彻底没有任何访问记录,因为咱们的部署是本机的nginx转到本机的tomcat应用,因此能够排除不是网络问题。
到此基本能够判定网络没有问题,tomcat 自己出现了假死的状况。在tomcat的日志里有报过OutOfMemoryError的异常,因此能够确定tomcat假死的缘由是OOM
在咱们学习Java的时候就知道它最为方便的地方就是咱们不须要管理内存的分配和释放,一切由JVM本身来进行处理,当Java对象再也不被应用时,等到堆内存不够用时JVM会进行GC处理,
清除这些对象占用的堆内存空间,可是若是对象一直被应用,那么JVM是没法对其进行GC处理的,那么咱们建立新的对象时,JVM就没有办法从堆中获取足够的内存分配给此对象,这时就会致使OOM。
咱们出现OOM缘由,通常都是由于咱们不断的往容器里存放对象,然而容器没有相应的大小限制或清除机制,这样就容易致使OOM。
当咱们的应用服务器占用了过多内存的时候,咱们怎么样才能快速的定位问题呢?要想快速定位问题,首先咱们必需获取服务器JVM某时刻的内存快照。
Jdk里面提供了不少相应的命令好比:jstack,jstat,jmap,jps等等. 在出现问题后咱们应该快速保留现场。
能够观察到jvm中当前全部线程的运行状况和线程当前状态.
sudo jstack -F 进程ID
输出内容以下:
从上面的图咱们能够看到tomcat进程里面没有死锁的状况,并且每一个线程都处理等待的状态。这个时候咱们能够telnet命令连上tomcat的端口查看tomcat进程是否有任务回应。
这时发现tomcat没有任何回应能够证实tomcat应用已没有响应处理假死状态。
在thread dump中,要留意下面几种状态
死锁,Deadlock(重点关注)
等待资源,Waiting on condition(重点关注)
• 等待获取监视器,Waiting on monitor entry(重点关注)
阻塞,Blocked(重点关注)
• 执行中,Runnable
• 暂停,Suspended
• 对象等待中,Object.wait() 或 TIMED_WAITING
• 中止,Parked
这是jdk命令中比较重要,也是至关实用的一个命令,能够观察到classloader,compiler,gc相关信息
具体参数以下:
-class:统计class loader行为信息
-compile:统计编译行为信息
-gc:统计jdk gc时heap信息
-gccapacity:统计不一样的generations(包括新生区,老年区,permanent区)相应的heap容量状况
-gccause:统计gc的状况,(同-gcutil)和引发gc的事件
-gcnew:统计gc时,新生代的状况
-gcnewcapacity:统计gc时,新生代heap容量
-gcold:统计gc时,老年区的状况
-gcoldcapacity:统计gc时,老年区heap容量
-gcpermcapacity:统计gc时,permanent区heap容量
-gcutil:统计gc时,heap状况
-printcompilation:不知道干什么的,一直没用过。
通常比较经常使用的几个参数是:
sudo jstat -class 2083 1000 10 (每隔1秒监控一次,一共作10次)
查看当时的head状况
sudo jstat -gcutil 20683 2000
注:该图不是出错截取
出现时候截取的数据是gc已经彻底没有处理了,由于没有加上full gc的日志因此不肯定JVMGC 时间过长,致使应用暂停.
Jdk自带的jmap能够获取内在某一时刻的快照 命令:jmap -dump:format=b,file=heap.bin <pid> file:保存路径及文件名 pid:进程编号(windows经过任务管理器查看,linux经过ps aux查看) dump文件能够经过MemoryAnalyzer分析查看,网址:http://www.eclipse.org/mat/,能够查看dump时对象数量,内存占用,线程状况等。 从上面的图能够看得出来对象没有内存溢出。 从上图咱们能够明确的看出此项目的HashMap内存使用率比较高,由于咱们的系统都是返回Map的数据结构因此占用比较高的内存是正常状况。
观察运行中的jvm物理内存的占用状况。咱们也能够用jmap命令
参数以下:
-heap:打印jvm heap的状况
-histo:打印jvm heap的直方图。其输出信息包括类名,对象数量,对象占用大小。
-histo:live :同上,可是只答应存活对象的状况
-permstat:打印permanent generation heap状况
命令使用:
jmap -heap 2083
能够观察到New Generation(Eden Space,From Space,To Space),tenured generation,Perm Generation的内存使用状况
输出内容:
上图为tomcat应用出错前JVM的配置信息,能够明确的看到当时的信息:
MaxHeapSize堆内存大小为:3500M
MaxNewSize新生代内存大小:512M
PermSize永久代内存大小:192M
NewRatio设置年轻代(包括Eden和两个Survivor区)与年老代的比值(除去持久代)。设置为2,则年轻代与年老代所占比值为1:2,年轻代占整个堆栈的1/3
SurvivorRatio设置年轻代中Eden区与Survivor区的大小比值。设置为8,则两个Survivor区与一个Eden区的比值为2:8,一个Survivor区占整个年轻代的1/10
在New Generation中,有一个叫Eden的空间,主要是用来存放新生的对象,还有两个SurvivorSpaces(from,to), 它们用来存放每次垃圾回收后存活下来的对象。在Old Generation中,
主要存放应用程序中生命周期长的内存对象,还有个Permanent Generation,主要用来放JVM本身的反射对象,好比类对象和方法对象等。
从上面的图能够看出来JVM的新生代设置过小,能够看出应用的新生代区彻底占满了,没法再往新生代区增长新的对象此时的这些对象都处于活跃状态,因此不会被GC处理,可是tomcat应用还在继续产生新的对象,
这样就会致使OOM的发生,这就是致使tomcat假死的缘由.
如下是网上资料说的tomcat假的状况:
一、应用自己程序的问题,形成死锁。
二、load 过高,已经超出服务的极限
三、jvm GC 时间过长,致使应用暂停
由于出错项目里面没有打出GC的处理状况,因此不肯定此缘由是否也是我项目tomcat假死的缘由之一。
四、大量tcp 链接 CLOSE_WAIT
netstat -n | awk '/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}'
TIME_WAIT 48
CLOSE_WAIT 2228
ESTABLISHED 86
============================================nginx
1) 调高web服务器的最大链接线程数,即打开tomcat的server.xml文件,设置 Connector的如下参数:minProcessors="70" maxProcessors="2000" acceptCount="2000"
2) 修改运行web服务器的机器的操做系统网络配置,即在注册表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters中增长如下两个DWORD参数:
tob_id_3433
MaxUserPort=fffe TcpTimedWaitDelay=1e 最后,从新启动计算机。