Java线程堆栈是一个运行中的Java应用程序的所有线程的一个快照。它会显示一些像当前的堆栈跟踪、状态以及线程名称之类的信息。线程列表中包括由JVM本身创建的线程(负责垃圾收集、信号处理等管理工作)和由应用程序创建的线程。
通过给JVM发送一个SIGQUIT信号,您可以得到一个线程堆。在Unix操作系统(Solaris/Linux/HP-Unix等)中,通过kill-3<pid>命令可以得到线程堆,(在启动脚本中将输出重定向到文件中是一个很好的习惯,start.sh>trace.log 2>&1)。在Windows操作系统中,您可以在命令窗口键入ctrl-break得到线程堆。线程堆会输出到JVM的stdout或者stderr。输出出线程堆之后,应用程序继续正常运行。当您给JVM发送SIGQUIT信号时,JVM的信号处理器会通过输出线程堆来响应这一信号。当程序运行的时候,您可以在任何点得到线程堆。
线程堆的一个例子
下面显示的就是一个使用Sun JVM 1.4.1的单线程应用程序中的线程堆的例子。main线程是主应用程序线程。所有其他的线程都是由JVM创建的,负责完成一些管理工作。当分析应用程序级的问题时,我们通常只关心应用程序线程。下面,我们来分析清单1中main线程的堆栈跟踪。
"main" prio=5 tid=0x002358B8 nid=0x7f8 runnable [6f000..6fc40]
at test.method1(test.java:10)
at test.main(test.java:5)
从这个代码片断中您可以看到,一个线程堆栈跟踪有一个名称、线程优先级(prio=5)、状态(runnable)、源代码行号,以及方法调用。从这个堆栈跟踪中可以得到如下结论:main线程执行test类的method1方法中的一些代码。而对method1方法的调用是由同一个类的main方法完成的。您也可以看到那些方法中确切的源代码行号。
在从一些更复杂的情况去分析线程堆之前,我们先来讨论那些可以在线程堆中看到的线程的不同状态以及它们的意义。
· runnable:当获得CPU的使用权时就可以运行或准备好运行的状态。
"Thread-5" daemon prio=6 tid=0x00aa4b88 nid=0x3f0 runnable [0x02def000..0x02def9e8]
at java.io.WinNTFileSystem.getBooleanAttributes(Native Method)
at java.io.File.exists(Unknown Source)
at com.xmliu.threadheap.ThreadPool$TaskQueue.getTask(ThreadPool.java:66)
- locked <0x230c39c8> (a com.xmliu.threadheap.ThreadPool$TaskQueue)
at com.xmliu.threadheap.ThreadPool$Worker.run(ThreadPool.java:41)
Thread-5" 正在正在执行ThreadPool$Worker.run()方法,并且获得了锁<0x230c39c8> 。
· waiting for monitor entry:等待获得锁。
"Thread-4" daemon prio=6 tid=0x00aae3d0 nid=0x9c0 waiting for monitor entry [0x02daf000..0x02dafa68]
at com.xmliu.threadheap.ThreadPool$TaskQueue.getTask(ThreadPool.java:59)
- waiting to lock <0x230c39c8> (a com.xmliu.threadheap.ThreadPool$TaskQueue)
at com.xmliu.threadheap.ThreadPool$Worker.run(ThreadPool.java:41)
"Thread-4" 在执行ThreadPool$Worker.run()时需要获得锁<0x230c39c8>(waiting to lock <0x230c39c8>),正在等待其他线程释放。
· waiting on condition:这种情况会出现在对Thread对象调用任何sleep() 方法。
"Thread-2" daemon prio=6 tid=0x00a952b8 nid=0xea4 waiting on condition [0x02d2f000..0x02d2fd68]
at java.lang.Thread.sleep(Native Method)
at com.xmliu.threadheap.ThreadPool$TaskQueue.getTask(ThreadPool.java:61)
- locked <0x22c01288> (a com.xmliu.threadheap.ThreadPool$TaskQueue)
at com.xmliu.threadheap.ThreadPool$Worker.run(ThreadPool.java:40)
"Thread-2" 调用了sleep()方法。
· Object.wait():这种情况出现在对Thread对象调用了任何wait()方法,释放了已获得的锁并等待其他线程的唤醒或等待超时后重新竞争锁。
"Thread-1" daemon prio=6 tid=0x00a94cc8 nid=0xf0c in Object.wait() [0x02cef000..0x02cef9e8]
at java.lang.Object.wait(Native Method)
- waiting on <0x22c01288> (a com.xmliu.threadheap.ThreadPool$TaskQueue)
at com.xmliu.threadheap.ThreadPool$TaskQueue.getTask(ThreadPool.java:61)
- locked <0x22c01288> (a com.xmliu.threadheap.ThreadPool$TaskQueue)
at com.xmliu.threadheap.ThreadPool$Worker.run(ThreadPool.java:40)
"Thread-1"在获得锁<0x22c01288> 后,调用wait()方法释放了该锁,等待其他线程的唤醒。
从线程堆栈中,我们就定位出死锁问题、线程挂起、JVM崩溃等等情况;还能帮助我们定位性能瓶颈,我们的线程都在干什么,哪些资源存在争用。比如我们的任务分发线程做了很多的事情,而工作线程大都在等待分发线程的任务,那么我们就知道需要优化以下分发线程的代码,处理工作交给工作线程来做,自己只做消耗很少资源的分发工作。
JAVA线程dump的分析: http://www.linuxidc.com/Linux/2009-01/18171.htm
产生 JAVA线程 dump, 用 JDK 5.0工具包中的 jstack <pid>.
在实际运行中,往往一次 dump的信息,还不足以确认问题。建议产生三次 dump信息,如果每次 dump都指向同一个问题,我们才确定问题的典型性。
java jstack dump 线程 介绍 解释: http://blog.csdn.net/a43350860/article/details/8134234
三个实例演示 Java Thread Dump 日志分析: http://www.cnblogs.com/zhengyun_ustc/archive/2013/01/06/dumpanalysis.html
- 死锁,Deadlock(重点关注)
- 执行中,Runnable
- 等待资源,Waiting on condition(重点关注)
- 等待获取监视器,Waiting on monitor entry(重点关注)
- 暂停,Suspended
- 对象等待中,Object.wait() 或 TIMED_WAITING
- 阻塞,Blocked(重点关注)
- 停止,Parked