遇到java程序跑不动怎么办,jstack是比较容易想到的一个工具,利用jstack来dump出一个线程堆栈快照,而后具体分析。html
通常的堆栈大概是由下面的部分组成的:java
1 "resin-22129" daemon prio=10 tid=0x00007fbe5c34e000 nid=0x4cb1 waiting on condition [0x00007fbe4ff7c000] 2 java.lang.Thread.State: WAITING (parking) 3 at sun.misc.Unsafe.park(Native Method) 4 at java.util.concurrent.locks.LockSupport.park(LockSupport.java:315) 5 at com.caucho.env.thread2.ResinThread2.park(ResinThread2.java:196) 6 at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:147) 7 at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118) 8 9 10 "Timer-20" daemon prio=10 tid=0x00007fe3a4bfb800 nid=0x1a31 in Object.wait() [0x00007fe3a077a000] 11 java.lang.Thread.State: TIMED_WAITING (on object monitor) 12 at java.lang.Object.wait(Native Method) 13 - waiting on <0x00000006f0620ff0> (a java.util.TaskQueue) 14 at java.util.TimerThread.mainLoop(Timer.java:552) 15 - locked <0x00000006f0620ff0> (a java.util.TaskQueue) 16 at java.util.TimerThread.run(Timer.java:505)
"resin-22129"
线程名称:若是使用 java.lang.Thread 类生成一个线程的时候,线程名称为 Thread-(数字) 的形式,这里是resin生成的线程;daemon
线程类型:线程分为守护线程 (daemon) 和非守护线程 (non-daemon) 两种,一般都是守护线程;prio=10
线程优先级:默认为5,数字越大优先级越高;tid=0x00007fbe5c34e000
JVM线程的id:JVM内部线程的惟一标识,经过 java.lang.Thread.getId()获取,一般用自增的方式实现;nid=0x4cb1
系统线程id:对应的系统线程id(Native Thread ID),能够经过 top 命令进行查看,现场id是十六进制的形式;waiting on condition
系统线程状态:这里是系统的线程状态;[0x00007fbe4ff7c000]
起始栈地址:线程堆栈调用的其实内存地址;java.lang.Thread.State: WAITING (parking)
JVM线程状态:这里标明了线程在代码级别的状态。系统线程有以下状态:数据库
死锁线程,通常指多个线程调用期间进入了相互资源占用,致使一直等待没法释放的状况。api
通常指该线程正在执行状态中,该线程占用了资源,正在处理某个操做,如经过SQL语句查询数据库、对某个文件进行写入等。网络
线程正处于阻塞状态,指当前线程执行过程当中,所须要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,能够理解为等待资源超时的线程。多线程
线程正处于等待资源或等待某个条件的发生,具体的缘由须要结合下面堆栈信息进行分析。oracle
(1)若是堆栈信息明确是应用代码,则证实该线程正在等待资源,通常是大量读取某种资源且该资源采用了资源锁的状况下,线程进入等待状态,等待资源的读取,或者正在等待其余线程的执行等。app
(2)若是发现有大量的线程都正处于这种状态,而且堆栈信息中得知正等待网络读写,这是由于网络阻塞致使线程没法执行,颇有多是一个网络瓶颈的征兆:工具
因此必定要结合系统的一些性能观察工具进行综合分析,好比netstat统计单位时间的发送包的数量,看是否很明显超过了所在网络带宽的限制;观察CPU的利用率,看系统态的CPU时间是否明显大于用户态的CPU时间。这些都指向因为网络带宽所限致使的网络瓶颈。oop
(3)还有一种常见的状况是该线程在 sleep,等待 sleep 的时间到了,将被唤醒。
Moniter 是Java中用以实现线程之间的互斥与协做的主要手段,它能够当作是对象或者class的锁,每一个对象都有,也仅有一个 Monitor。
从上图能够看出,每一个Monitor在某个时刻只能被一个线程拥有,该线程就是 "Active Thread",而其余线程都是 "Waiting Thread",分别在两个队列 "Entry Set"和"Waint Set"里面等待。其中在 "Entry Set" 中等待的线程状态是 waiting for monitor entry
,在 "Wait Set" 中等待的线程状态是 in Object.wait()
。
(1)"Entry Set"里面的线程。
咱们称被 synchronized
保护起来的代码段为临界区
当一个线程申请进入临界区时,它就进入了 "Entry Set" 队列中,这时候有两种可能性:
临界区的设置是为了保证其内部的代码执行的原子性和完整性,但由于临界区在任什么时候间只容许线程串行经过,这和咱们使用多线程的初衷是相反的。若是在多线程程序中大量使用synchronized,或者不适当的使用它,会形成大量线程在临界区的入口等待,形成系统的性能大幅降低。若是在Thread Dump中发现这个状况,应该审视源码并对其进行改进。
(2)"Wait Set"里面的线程
当线程得到了Monitor,进入了临界区以后,若是发现线程继续运行的条件没有知足,它则调用对象(一般是被synchronized的对象)的wait()方法,放弃Monitor,进入 "Wait Set"队列。只有当别的线程在该对象上调用了 notify()或者notifyAll()方法,"Wait Set"队列中的线程才获得机会去竞争,可是只有一个线程得到对象的Monitor,恢复到运行态。"Wait Set"中的线程在Thread Dump中显示的状态为 in Object.wait()。一般来讲,
一般来讲,当CPU很忙的时候关注 Runnable 状态的线程,反之则关注 waiting for monitor entry 状态的线程。
在 java.lang.Thread.State 中定义了线程的状态:
至今还没有启动的线程的状态。线程刚被建立,但还没有启动。
可运行线程的线程状态。线程正在JVM中执行,有可能在等待操做系统中的其余资源,好比处理器。
受阻塞而且正在等待监视器的某一线程的线程状态。处于受阻塞状态的某一线程正在等待监视器锁,以便进入一个同步的块/方法,或者在调用 Object.wait 以后再次进入同步的块/方法。
在Thread Dump日志中一般显示为 java.lang.Thread.State: BLOCKED (on object monitor) 。
某一等待线程的线程状态。线程正在无期限地等待另外一个线程来执行某一个特定的操做,线程由于调用下面的方法之一而处于等待状态:
指定了等待时间的某一等待线程的线程状态。线程正在等待另外一个线程来执行某一个特定的操做,并设定了指定等待的时间,线程由于调用下面的方法之一而处于定时等待状态:
线程处于终止状态。
根据Java Doc中的说明,在给定的时间上,一个只能处于上述的一种状态之中,而且这些状态都是JVM的状态,跟操做系统中的线程状态无关。
参考
https://go-on.iteye.com/blog/1673894
http://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html
https://www.javatang.com/archives/2017/10/25/36441958.html
https://www.cubrid.org/blog/the-principles-of-java-application-performance-tuning