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命令查看对应进程是哪个线程占欢迎分享,转载请注明来源:内存溢出
评论列表(0条)