Java死锁和Java进程Java CPU 100%排查

三板斧:top -> top -Hp ->jstack

  • 通过 top 命令找到 CPU 消耗最多的进程号;
  • 通过 top -Hp 进程号 命令找到 CPU 消耗最多的线程号(列名仍然为 PID);
  • 通过printf "%x\n" 线程号 命令输出该线程号对应的 16 进制数字;
  • 通过 jstack 进程号 | grep 16进制线程号  -A 10 命令找到 CPU 消耗最多的线程方法堆栈。

1、确定Java应用进程编号

使用 jps 或 ps -ef|grep java 命令确定想要分析的应用的进程编号。

2、使用死锁检测工具检测死锁
2.1 Jstack命令

jstack是java虚拟机自带的一种堆栈跟踪工具。jstack用于打印出给定的java进程ID或core file或远程调试服务的Java堆栈信息。 Jstack工具可以用于生成java虚拟机当前时刻的线程快照。线程快照是当前java虚拟机内每一条线程正在执行的方法堆栈的集合,生成线程快照的主要目的是定位线程出现长时间停顿的原因,如线程间死锁、死循环、请求外部资源导致的长时间等待等。 线程出现停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道没有响应的线程到底在后台做什么事情,或者等待什么资源。

2.2 JConsole工具

Jconsole是JDK自带的监控工具,在JDK/bin目录下可以找到。它用于连接正在运行的本地或者远程的JVM,对运行在Java应用程序的资源消耗和性能进行监控,并画出大量的图表,提供强大的可视化界面。而且本身占用的服务器内存很小,甚至可以说几乎不消耗。

2.3 Java Visual VM

启动 VisualVM,在应用程序窗口,选择对应的JAVA应用,在详情窗口》线程标签(勾选线程可视化),查看线程生命周期状态,主要留意线程生命周期中红色部分。

(1)绿色:代表运行状态。一般属于正常情况。如果是多线程环境,生产者消费者模式下,消费者一直处于运行状态,说明消费者处理性能低,跟不上生产者的节奏,需要优化对应的代码,如果不处理,就可能导致消费者队列阻塞的现象。对应线程的【RUNNABLE】状态。

(2)蓝色:代表线程休眠。线程中调用Thread.sleep()函数的线程状态时,就是蓝色。对应线程的【TIMED_WAITING】状态。

(3)黄色:代表线程等待。调用线程的wait()函数就会出现黄色状态。对应线程的【WAITING】状态。

(4)红色:代码线程锁定。对应线程的【BLOCKED】状态。

检测到死锁之后我们可以dump下线程日志分析死锁发生原因。

3、分析解决JAVA应用程序线程锁

        发生线程锁的原因有很多,我所遇到比较多的情况是多线程同时访问同一资源,且此资源使用synchronized关键字,导致一个线程要等另外一个线程使用完资源后才能运行。例如在没有连接池的情况下,同时访问数据库接口,这种情况会导致性能的极具下降,解决的方案是增加连接池,或者修改访问方式,或者将资源粒度细化,类似ConCurrentHashMap中的处理方式,将资源分为多个更小粒度的资源,在更小粒度资源上来处理锁,就可以解决资源竞争激烈的问题。

二、Java CPU 100%

在linux环境下部署的应用,有时候出于各种原因,出现cpu占用100%的情况。这时候,就需要快速分析定位cpu占用的原因。通常,通过linux系统的top命令,可以看出具体哪个进程占用了过多的cpu资源。但如果发现是java进程,那么就需要进一步分析是java进程中的具体哪个线程出现了问题。

1、使用top命令查看是哪个进程占用过多的cpu资源

2、确定Java应用(此处可省略,直接使用上一步top中的32220作为pid1即可)

使用 jps 或 ps -ef|grep java 命令确定想要分析的应用的进程编号[pid1]

3、查看Java应用中线程CPU占比

使用top -Hp [pid1] 命令查看指定进程下的线程cpu占用比例,分析是具体哪个线程占用率过高,记下其进程id(pid2),其中 [pid2] 就是通过第一步确定下来的进程编号,转成16进制。

pid转成16进制命令(32230->7de6):

printf “%x\n” [pid2]

4、使用jstack查看线程信息

从中选择占比较高的线程的编号(PID),并将该PID转换为16进制,通过jstack [pid1] |grep -A 10 0x7de6

更多时候我们需要将日志dump下来进行分析,导出线程栈命令:

 jstack -l 32220 > jstack-32220.log

jstack Dump 日志文件中的线程状态

1:dump 文件里,值得关注的线程状态有(标红部分需要重点关注)

  1. 死锁, Deadlock
  2. 执行中,Runnable   
  3. 等待资源, Waiting on condition 
  4. 等待获取监视器, Waiting on monitor entry
  5. 暂停,Suspended
  6. 对象等待中,Object.wait() 或 TIMED_WAITING
  7. 阻塞, Blocked
  8. 停止,Parked

2:Dump文件中的线程状态含义及注意事项

  • Deadlock:死锁线程,一般指多个线程调用间,进入相互资源占用,导致一直等待无法释放的情况。
  • Runnable:一般指该线程正在执行状态中,该线程占用了资源,正在处理某个请求,有可能正在传递SQL到数据库执行,有可能在对某个文件操作,有可能进行数据类型等转换。
  • Waiting on condition:该状态出现在线程等待某个条件的发生。具体是什么原因,可以结合 stacktrace来分析。最常见的情况是线程在等待网络的读写,比如当网络数据没有准备好读时,线程处于这种等待状态,而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。在 Java引入 NewIO之前,对于每个网络连接,都有一个对应的线程来处理网络的读写操作,即使没有可读写的数据,线程仍然阻塞在读写操作上,这样有可能造成资源浪费,而且给操作系统的线程调度也带来压力。在 NewIO里采用了新的机制,编写的服务器程序的性能和可扩展性都得到提高。如果发现有大量的线程都在处在 Wait on condition,从线程 stack看, 正等待网络读写,这可能是一个网络瓶颈的征兆。因为网络阻塞导致线程无法执行。一种情况是网络非常忙,几 乎消耗了所有的带宽,仍然有大量数据等待网络读 写;另一种情况也可能是网络空闲,但由于路由等问题,导致包无法正常的到达。所以要结合系统的一些性能观察工具来综合分析,比如 netstat统计单位时间的发送包的数目,如果很明显超过了所在网络带宽的限制 ; 观察 cpu的利用率,如果系统态的 CPU时间,相对于用户态的 CPU时间比例较高;如果程序运行在 Solaris 10平台上,可以用 dtrace工具看系统调用的情况,如果观察到 read/write的系统调用的次数或者运行时间遥遥领先;这些都指向由于网络带宽所限导致的网络瓶颈。另外一种出现 Wait on condition的常见情况是该线程在 sleep,等待 sleep的时间到了时候,将被唤醒。
  • locked:线程阻塞,是指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。
  • Waiting for monitor entry 和 in Object.wait():Monitor是 Java中用以实现线程之间的互斥与协作的主要手段,它可以看成是对象或者 Class的锁。每一个对象都有,也仅有一个 monitor。

垃圾回收统计

容量(Capacity)和使用量(Used),大小单位(KB),时间单位(s)

S0C:第一个幸存区的大小
S1C:第二个幸存区的大小
S0U:第一个幸存区的使用大小
S1U:第二个幸存区的使用大小
EC:伊甸园区的大小
EU:伊甸园区的使用大小
OC:老年代大小
OU:老年代使用大小
MC:方法区大小
MU:方法区使用大小
CCSC:压缩类空间大小
CCSU:压缩类空间使用大小
YGC:年轻代垃圾回收次数
YGCT:年轻代垃圾回收消耗时间
FGC:老年代垃圾回收次数
FGCT:老年代垃圾回收消耗时间
GCT:垃圾回收消耗总时间

猜你喜欢

转载自blog.csdn.net/u014553029/article/details/105746473