一.引言
1.编写目的
为了方便大家以后发现进程假死的时候能够正常的分析并且第一时间保留现场快照。
2.编写背景
最近服务器发现tomcat的应用会偶尔出现无法访问的情况。经过一段时间的观察最近又发现有台tomcat的应用出现了无法访问情况。简单描述下该台tomcat当时具体的表现:客户端请求没有响应,查看服务器端tomcat的进程是存活的,查看业务日志的时候发现日志停止没有任何最新的访问日志。连tomcat下面的catalina.log也没有任何访问记录,基本断定该台tomcat已不能提供服务。
二.分析步骤
根据前面我描述的假死现象,我最先想到的是网络是否出现了问题,是不是有什么丢包严重的情况,于是我开始从请求的数据流程开始分析,由于我们业务的架构采用的是nginx+tomcat的集群配置,一个请求上来的流向可以用下图来简单的描述一下:
1.检查nginx的网络情况
更改nginx的配置,让该台nginx请求只转到本机器的出现问题的tomcat应用上面,在access.log里看是否有网络请求,结果可以查看到当前所有的网络请求,也就是说可以排除是网络的问题。
2.检查tomcat 的网络情况
分析业务配置的tomcat访问日志xxxx.log上是否有日志访问记录,经过查询该台tomcat应用日志完全没有任何访问记录,由于我们的部署是本机的nginx转到本机的tomcat应用,所以可以排除不是网络问题。到此基本可以断定网络没有问题,tomcat 本身出现了假死的情况。在tomcat的日志里有报过OutOfMemoryError的异常,所以可以肯定tomcat假死的原因是OOM
三.分析JVM内存溢出
1.为什么会发生内存泄漏
在我们学习Java的时候就知道它最为方便的地方就是我们不需要管理内存的分配和释放,一切由JVM自己来进行处理,当Java对象不再被应用时,等到堆内存不够用时JVM会进行GC处理,清除这些对象占用的堆内存空间,但是如果对象一直被应用,那么JVM是无法对其进行GC处理的,那么我们创建新的对象时,JVM就没有办法从堆中获取足够的内存分配给此对象,这时就会导致OOM。我们出现OOM原因,一般都是因为我们不断的往容器里存放对象,然而容器没有相应的大小限制或清除机制,这样就容易导致OOM。
2.快速定位问题
当我们的应用服务器占用了过多内存的时候,我们怎么样才能快速的定位问题呢?要想快速定位问题,首先我们必需获取服务器JVM某时刻的内存快照。Jdk里面提供了很多相应的命令比如:jstack,jstat,jmap,jps等等. 在出现问题后我们应该快速保留现场。
1).jstack
可以观察到jvm中当前所有线程的运行情况和线程当前状态.
sudo jstack -F 进程ID
输出内容如下:
从上面的图我们可以看到tomcat进程里面没有死锁的情况,而且每个线程都处理等待的状态。这个时候我们可以telnet命令连上tomcat的端口查看tomcat进程是否有任务回应。这时发现tomcat没有任何回应可以证明tomcat应用已没有响应,处于假死状态。
2)jstat
这是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的日志所以不确定JVM GC 时间过长,导致应用暂停。
3)获取内存快照
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的数据结构所以占用比较高的内存是正常情况。
4).观察运行中的jvm物理内存的占用情况
观察运行中的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的空间,主要是用来存放新生的对象,还有两个Survivor Spaces(from,to), 它们用来存放每次垃圾回收后存活下来的对象。在Old Generation中,主要存放应用程序中生命周期长的内存对象,还有个Permanent Generation,主要用来放JVM自己的反射对象,比如类对象和方法对象等。
从上面的图可以看出来JVM的新生代设置太小,可以看出应用的新生代区完全占满了,无法再往新生代区增加新的对象此时的这些对象都处于活跃状态,所以不会被GC处理,但是tomcat应用还在继续产生新的对象,这样就会导致OOM的发生,这就是导致tomcat假死的原因。
四.Tomcat假死其它情况
以下是网上资料说的tomcat假的情况:
1.应用本身程序的问题,造成死锁。
2.load 太高,已经超出服务的极限。
3.jvm GC 时间过长,导致应用暂停。
因为出错项目里面没有打出GC的处理情况,所以不确定此原因是否也是我项目tomcat假死的原因之一。
4.大量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
常用的三个状态是:ESTABLISHED 表示正在通信,TIME_WAIT 表示主动关闭,CLOSE_WAIT 表示被动关。
相关推荐
脚本内容:java_check.sh #! /bin/bash # if [ -z `pidof java` ]; then echo -e `date +%Y-%m-%d` `date +%R`. “33[31m33[1m(JAVA)Process does not exist! 33[0m” >> /data/logs/java_check.log /usr/local/...
在Java编程中,有时我们需要通过Java进程执行外部程序来完成特定任务,比如文档转换、数据处理等。然而,这样的操作可能会遇到一个常见的问题,即Java进程执行外部程序时发生阻塞,导致程序运行停滞。这个问题可能由...
在Java开发中,特别是在创建图形用户界面(GUI)的应用程序时,可能会遇到界面假死(也称为冻结或无响应)的问题。这种情况通常是由于主线程(事件分发线程,EDT)被长时间阻塞,无法处理用户的交互请求,导致应用...
本文主要探讨了如何解决CentOS服务器上存在的大量TIME_WAIT TCP连接问题,这可能导致服务器连接数过多,进而引起服务假死。当服务器之间的通信过于频繁,如通过REST请求互相调用时,Java服务器可能无法及时回收TCP...
- `&&`是一个逻辑运算符,只用于布尔表达式,支持短路逻辑,即如果第一个条件为假,则第二个条件不会被计算。 4. **跳出多重嵌套循环**: - 使用标签化`break`语句可以实现。例如,可以在外层循环之前定义一个...
- **解析**: 在Java中,线程是轻量级的进程,多个线程可以在同一进程中并发执行。线程共享进程的资源,但有独立的执行路径。 12. **在方法头用abstract修饰符进行修饰的方法叫做** `抽象` **方法。** - **知识点*...
在Java中,`&&`和`&`都可以用来表示逻辑与运算,但是`&&`具有短路效应,即如果第一个条件为假,则不会评估第二个条件。而`&`则会一直计算到最后一个表达式,即使前面的条件已经足以确定结果。理解这两种运算符的区别...
- 可以避免因特定线程未被唤醒而导致的“假死”问题。 #### 九、如何保证三个线程按顺序执行 - **使用CountDownLatch**: - 创建CountDownLatch对象,计数器设为2。 - 第一线程执行完毕后调用countDown,计数器...
- **线程**: Java中的线程是一种轻量级进程,它允许程序在同一时间执行多个任务。Java通过`Thread`类提供了创建线程的基本机制。线程可以分为用户线程和守护线程。 - **封装**: 封装是面向对象编程的一个核心概念,...
3. Tomcat 假死案例分析 2.2.1 发现问题:监控平台发现某个 Tomcat 节点已经无法采集到数据,连接服务器进程还在,nestat -anop|grep 8001 端口也在监听,查看日志打印时断时续。 2.2.2 查询日志:查看 NG 日志,...
- `wait()`应置于`while`循环中,防止假唤醒。 18. **`yield()`**: - 当前线程主动让出执行权,但不保证立即执行其他线程。 19. **Servlet线程安全性**: - 单例的Servlet(如Tomcat默认)是线程不安全的,...
而`&&`是短路与运算符,如果左边的操作数为假,则右边的操作数不会被计算,这可以提高效率并避免不必要的计算。 #### 4\. 跳出多重嵌套循环 在Java中,可以通过使用标记(label)和`break`语句来跳出多重嵌套循环。...
- Oracle数据库提供了一个名为`EXPLAIN PLAN`的工具,用于分析SQL语句的执行计划。 6. **数据字典** - 数据字典是Oracle数据库中的一个特殊类型的表,用于存储关于数据库的所有信息。 7. **PL/SQL** - PL/SQL...
&&是短路与,如果左边为假,右边不再执行。 14. **Error与Exception**:Error是严重错误,如系统错误,通常不捕获;Exception是程序运行时出现的异常,可捕获处理。 15. **线程同步**:包括synchronized、wait/...
- **线程的基本概念**:线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。 - **线程的生命周期**:线程有多种状态,包括新建、就绪、运行、阻塞和终止。 - **线程安全问题**...
1. **日志分析**:定期分析Tomcat的日志,找出导致服务假死或异常的常见原因,优化系统配置或代码以减少这类问题的发生。 2. **故障转移**:如果可能,可以设置Tomcat集群,当一个节点出现问题时,流量可以自动切换...