ideatomcat启动cpu就100%了

ideatomcat启动cpu就100%了,第1张

线上出现tomcat的CPU出现100%,排查及其解决的步骤

1、输入top,查看cpu的占用比率

2、通过ps aux | grep PID或ps -ef |grep PID命令,定位具体的进程主体,如是否是tomcat启动的java进程出现了问题。

3、用ps -mp PID -o THREAD,tid,time命令打印出该进程下的线程占用cpu情况

4、用printf “%x\n” TID命令将需要的线程ID转换为16进制格式

5、最后用jstack pid |grep tid -A 100 命令打印线程的堆栈信息:

6、逐条分析CPU占用分析

1、RUNNABLE

表示线程具备所有运行条件,在运行队列中准备 *** 作系统的调度,或者正在运行

"http-9601-Acceptor-0" daemon prio=10 tid=0x00007f69ac573000 nid=0x284d runnable [0x00007f699efae000]

java.lang.Thread.State: RUNNABLE

at java.net.PlainSocketImpl.socketAccept(Native Method)

at java.net.AbstractPlainSocketImpl.accept(AbstractPlainSocketImpl.java:398)

at java.net.ServerSocket.implAccept(ServerSocket.java:530)

at java.net.ServerSocket.accept(ServerSocket.java:498)

at org.apache.tomcat.util.net.DefaultServerSocketFactory.acceptSocket(DefaultServerSocketFactory.java:61)

at org.apache.tomcat.util.net.JIoEndpoint$Acceptor.run(JIoEndpoint.java:366)

at java.lang.Thread.run(Thread.java:745)

线程名称:http-9601-Acceptor-0

线程类型:daemon (守护线程)

优先级:prio=10 默认是5

JVM线程Id:tid=0x00007f69ac573000 JVM内部线程的唯一标示(通过java.lang.Thead.getId()获取,通常用自增方式实现)

对应系统线程Id(nativeThread ID):nid=0x284d,和top名称查看的线程pid对应,一个是10进制,一个是16进制

线程状态:runnable

起始栈地址:[0x00007f699efae000]

Java thread statck trace :精确定位问题的信息来源。需要逆向解读。

2、WAITING (on object monitor)

"http-9601-1" daemon prio=10 tid=0x00007f68f8001000 nid=0x284e in Object.wait() [0x00007f699ef6d000]

java.lang.Thread.State: WAITING (on object monitor)

at java.lang.Object.wait(Native Method)

- waiting on <0x00000007012edf08>(a org.apache.tomcat.util.net.JIoEndpoint$Worker)

at java.lang.Object.wait(Object.java:503)

at org.apache.tomcat.util.net.JIoEndpoint$Worker.await(JIoEndpoint.java:472)

- locked <0x00000007012edf08>(a org.apache.tomcat.util.net.JIoEndpoint$Worker)

at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:498)

at java.lang.Thread.run(Thread.java:745)

仔细观察上面的 DUMP信息,你会发现它有以下两行:

locked <0x00000007012edf08>(a org.apache.tomcat.util.net.JIoEndpoint$Worker)

waiting on <0x00000007012edf08>(a org.apache.tomcat.util.net.JIoEndpoint$Worker)

这里需要解释一下,为什么先 lock了这个对象,然后又 waiting on同一个对象呢?让我们看看这个线程对应的代码:

synchronized(obj){

………

obj.wait()

………

}

线程的执行中,先用 synchronized 获得了这个对象的 Monitor(对应于 locked <0x00000007012edf08>)。当执行到 obj.wait(), 线程即放弃了 Monitor的所有权,进入 “wait set”队列(对应于waiting on <0x00000007012edf08>)。

往在你的程序中,会出现多个类似的线程,他们都有相似的 dump也可能是正常的。比如,在程序中有多个服务线程,设计成从一个队列里面读取请求数据。这个队列就是 lock以及 waiting on的对象。当队列为空的时候,这些线程都会在这个队列上等待,直到队列有了数据,这些线程被notify,当然只有一个线程获得了 lock,继续执行,而其它线程继续等待。

org.apache.tomcat.util.net.JIoEndpoint的讲解参考地址: ​

3、Blocked

阻塞状态是指线程因为某些原因放弃CPU,暂时停止运行。当线程处于阻塞状态时,Java虚拟机不会给线程分配CPU。直到线程重新进入就绪状态,它才有机会转到运行状态。

阻塞状态可分为以下3种:

1)位于对象等待池中的阻塞状态(Blocked in object’s wait pool):当线程处于运行状态时,如果执行了某个对象的wait()方法,Java虚拟机就会把线程放到这个对象的等待池中,这涉及到“线程通信”的内容。

2)位于对象锁池中的阻塞状态(Blocked in object’s lock pool):当线程处于运行状态时,试图获得某个对象的同步锁时,如果该对象的同步锁已经被其他线程占用,Java虚拟机就会把这个线程放到这个对象的锁池中,这涉及到“线程同步”的内容。

3)其他阻塞状态(Otherwise Blocked):当前线程执行了sleep()方法,或者调用了其他线程的join()方法,或者发出了I/O请求时,就会进入这个状态。

4、wait on condition

The thread is either sleeping or waiting to be notified by another thread.

改状态出现在线程等待某个条件的发生或者sleep。具体是什么原因,可以结合stacktrace来分析。最常用的情况是线程在等待网络的读写,比如当网络数据还没有准备好读时,线程处于这种等待状态,而一旦有数据准备好读之后,线程会重新激活,读取并处理数据。如果发现有大量的线程都处在wait on condition,从线程stack看,正等待网络读写,这可能是一个网络瓶颈的征兆。

5、Waiting for Monitor Entry and in Object.wait()

在多线程的java程序中,实现线程之间的同步,就要说说monitor,Monitor是java中用意实现线程之间的互斥与协作的主要手段,它可以看成是对象或者class的锁。每个对象都有,也仅有一个monitor。每个monitor在某个时刻,只能被一个线程拥有,该线程就是activeThread,而其他线程都是waiting thread,分别在两个队列中“entry set” 和“wait set”里面等候。在“entry set”中等待的线程状态是“waiting for monitorentry”而在“wait set”中等待的线程状态是“in Object.wait()”

先看 “Entry Set”里面的线程。我们称被 synchronized保护起来的代码段为临界区。当一个线程申请进入临界区时,它就进入了 “Entry Set”队列。对应的 code就像:

synchronized(obj) {

………

}

这时有两种可能性:

该 monitor不被其它线程拥有, Entry Set里面也没有其它等待线程。本线程即成为相应类或者对象的 Monitor的 Owner,执行临界区的代码。

该 monitor被其它线程拥有,本线程在 Entry Set队列中等待。

在第一种情况下,线程将处于 “Runnable”的状态,而第二种情况下,线程 DUMP会显示处于 “waiting for monitor entry”。

临界区的设置,是为了保证其内部的代码执行的原子性和完整性。但是因为临界区在任何时间只允许线程串行通过,这和我们多线程的程序的初衷是相反的。如果在多线程的程序中,大量使用 synchronized,或者不适当的使用了它,会造成大量线程在临界区的入口等待,造成系统的性能大幅下降。如果在线程 DUMP中发现了这个情况,应该审查源码,改进程序。

再看“Wait Set”里面的线程。当线程获得了 Monitor,进入了临界区之后,如果发现线程继续运行的条件没有满足,它则调用对象(一般就是被 synchronized 的对象)的 wait() 方法,放弃 Monitor,进入 “Wait Set”队列。只有当别的线程在该对象上调用了 notify() 或者 notifyAll(),“Wait Set”队列中线程才得到机会去竞争,但是只有一个线程获得对象的Monitor,恢复到运行态。在 “Wait Set”中的线程, DUMP中表现为: in Object.wait()。

一般,Cpu很忙时,则runnable的线程,Cpu很闲时,则waiting for monitor entry的线程。

"GC task thread#0 (ParallelGC)" prio=10 tid=0x00007f69ac01e800 nid=0x2814 runnable

"GC task thread#1 (ParallelGC)" prio=10 tid=0x00007f69ac020800 nid=0x2815 runnable

"GC task thread#2 (ParallelGC)" prio=10 tid=0x00007f69ac022800 nid=0x2816 runnable

"GC task thread#3 (ParallelGC)" prio=10 tid=0x00007f69ac024800 nid=0x2817 runnable

"GC task thread#4 (ParallelGC)" prio=10 tid=0x00007f69ac026800 nid=0x2818 runnable

"GC task thread#5 (ParallelGC)" prio=10 tid=0x00007f69ac028800 nid=0x2819 runnable

"GC task thread#6 (ParallelGC)" prio=10 tid=0x00007f69ac02a000 nid=0x281a runnable

"GC task thread#7 (ParallelGC)" prio=10 tid=0x00007f69ac02c000 nid=0x281b runnable

当使用HotSpot parallel GC,HotSpot VM默认创建一定数目的GC thread。当面对过多GC,内存泄露等问题时,这些是关键的数据。使用native id,可以将从OS/Java进程观测到的高CPU与这些线程关联起来。

JNI global references count

JNI global references: 570

JNI global reference是基本的对象引用,从本地代码到被Java GC管理的Java对象的引用。其角色是阻止仍然被本地代码使用的对象集合,但在Java代码中没有引用。

在探测JNI相关内存泄露时,JNI references很重要。如果你的程序直接使用JNI或使用第三方工具,如检测工具,检测本地内存泄露。

1、首先dump出该进程的所有线程及状态使用命令jstackPID命令打印出CPU占用过高进程的线程栈.jstack-l5683>5683.stack将进程id为5683的线程栈输出到了文件2、使用top命令找到耗cpu的线程使用top-H-pPID命令查看对应进程是哪个线程占


欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/yw/11935022.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2023-05-19
下一篇 2023-05-19

发表评论

登录后才能评论

评论列表(0条)

保存