线程堆栈也称作线程调用堆栈。Java线程堆栈是虚拟机中线程(包括锁)状态的一个瞬间快照,即系统在某个时刻所有线程的运行状态,包括每一个线程的调用堆栈,锁的持有情况等信息。对于已经消失而又没留有痕迹的信息,线程堆栈是无法进行历史追踪的。
线程堆栈的信息包括:
(1)线程的名字,id,线程的数量等
(2)线程的运行状态,锁的状态(锁被哪个线程持有,哪个线程再等待锁等)
(3)调用堆栈(即函数的调用层次关系),调用堆栈包括完整的类名,所执行的方法,源代码的行数。
借助线程堆栈可以分析:线程死锁、锁争用、死循环、识别耗时操作、稳定性分析和性能分析等问题

线程作用:

因为线程栈是瞬时快照包含线程状态以及调用关系,所以借助堆栈信息可以帮助分析很多问题,比如线程死锁,锁争用,死循环,识别耗时操作等等。线程栈是瞬时记录,所以没有历史消息的回溯,一般我们都需要结合程序的日志进行跟踪,一般线程栈能分析如下性能问题:

1、系统无缘无故的cpu过高
2、系统挂起,无响应
3、系统运行越来越慢
4、性能瓶颈(如无法充分利用cpu等)
5、线程死锁,死循环等
6、由于线程数量太多导致的内存溢出(如无法创建线程等)

比如有如下堆栈实例:

"resin-22129" daemon prio=10 tid=0x00007fbe5c34e000 nid=0x4cb1 waiting on condition [0x00007fbe4ff7c000]java.lang.Thread.State: WAITING (parking)at sun.misc.Unsafe.park(Native Method)at java.util.concurrent.locks.LockSupport.park(LockSupport.java:315)at com.caucho.env.thread2.ResinThread2.park(ResinThread2.java:196)at com.caucho.env.thread2.ResinThread2.runTasks(ResinThread2.java:147)at com.caucho.env.thread2.ResinThread2.run(ResinThread2.java:118)"Timer-20" daemon prio=10 tid=0x00007fe3a4bfb800 nid=0x1a31 in Object.wait() [0x00007fe3a077a000]java.lang.Thread.State: TIMED_WAITING (on object monitor)at java.lang.Object.wait(Native Method)- waiting on <0x00000006f0620ff0> (a java.util.TaskQueue)at java.util.TimerThread.mainLoop(Timer.java:552)- locked <0x00000006f0620ff0> (a java.util.TaskQueue)at java.util.TimerThread.run(Timer.java:505)
  • “resin-22129” 线程名称。
  • daemon 线程类型:线程分为守护线程 (daemon) 和非守护线程 (non-daemon) 两种;
  • prio=10 线程优先级:默认为5,数字越大优先级越高。
  • tid=0x…000 JVM线程的id,通过Thread.getId()获取。
  • nid=0x4cb1 系统线程id:对应的系统线程id,可以通过 top命令进行查看的pid对应,线程id是十六进制的形式;
  • waiting on condition 操作系统线程状态:具体含义见下面分析。
  • [0x00007fbe4ff7c000] 起始栈地址:线程堆栈调用的起始内存地址;
  • java.lang.Thread.State: WAITING (parking) JVM线程状态:具体见下面分析。
  • 线程此时正在执行的方法,以及调用链
    at sun.misc.Unsafe.park(Native Method)
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:315)
    at com.caucho.env.thread2.ResinThread2.park(ResinThread2.java:196)

操作系统线程状态

(1)deadlock
死锁线程,一般指多个线程调用期间进入了相互资源占用,导致一直等待无法释放的情况。
(2)runnable
一般指该线程正在执行状态中,该线程占用了资源。
(3)blocked
线程正处于阻塞状态,指当前线程执行过程中,所需要的资源长时间等待却一直未能获取到,被容器的线程管理器标识为阻塞状态,可以理解为等待资源超时的线程。
(4)waiting on condition
线程正处于等待资源或等待某个条件的发生。
(5)waiting for monitor entry 或 in Object.wait()
java内置锁(synchronized)

jvm线程状态

(1)NEW
线程刚刚被创建,也就是已经new过了,但是还没有调用start()方法,这个状态我们使用jstack进行线程栈dump的时候基本看不到,因为是线程刚创建时候的状态。
(2)RUNNABLE
从虚拟机的角度看,线程正在运行状态,状态是线程正在正常运行中, 当然可能会有某种耗时计算/IO等待的操作/CPU时间片切换等, 这个状态下发生的等待一般是其他系统资源, 而不是锁, Sleep等。
处于RUNNABLE状态的线程是不是一定会消耗cpu呢,不一定,像socket IO操作,线程正在从网络上读取数据,尽管线程状态RUNNABLE,但实际上网络io,线程绝大多数时间是被挂起的,只有当数据到达后,线程才会被唤起,挂起发生在本地代码(native)中,虚拟机根本不一致,不像显式的调用sleep和wait方法,虚拟机才能知道线程的真正状态,但在本地代码中的挂起,虚拟机无法知道真正的线程状态,因此一概显示为RUNNABLE。(3)BLOCKED
线程处于阻塞状态,正在等待一个monitor lock。
通常情况下,是因为本线程与其他线程公用了一个锁。其他在线程正在使用这个锁进入某个synchronized同步方法块或者方法,
而本线程进入这个同步代码块也需要这个锁,最终导致本线程处于阻塞状态。真实生活例子:今天你要去阿里面试。这是你梦想的工作,你已经盯着它多年了。你早上起来,准备好,穿上你最好的外衣,对着镜子打理好。当你走进车库发现你的朋友已经把车开走了。在这个场景,你只有一辆车,所以怎么办?在真实生活中,可能会打架抢车。现在因为你朋友把车开走了你被BLOCKED了。你不能去参加面试。这就是BLOCKED状态。用技术术语讲,你是线程T1,你朋友是线程T2,而锁是车。T1被BLOCKED在锁(例子里的车)上,因为T2已经获取了这个锁。(4)WAITING
这个状态下是指线程拥有了某个锁之后, 调用了他的wait方法, 等待其他线程/锁拥有者调用 notify / notifyAll一遍该线程可以继续下一步操作,这里要区分 BLOCKED 和 WATING 的区别, 一个是在临界点外面等待进入, 一个是在理解点里面wait等待别人notify, 线程调用了join方法 join了另外的线程的时候, 也会进入WAITING状态, 等待被他join的线程执行结束,处于waiting状态的线程基本不消耗CPU。小贴士:当线程调用以下方法时会进入WAITING状态1,不带超时的 Object.wait 方法。2,不带超时的 Thread.join 方法。3,LockSupport.park 方法。
(5)TIMED_WAITING
该线程正在等待,通过使用了 sleep, wait, join 或者是 park 方法。(这个与 WAITING 不同是通过方法参数指定了最大等待时间,WAITING 可以通过时间或者是外部的变化解除),线程等待指定的时间。真实生活例子:尽管这次面试过程充满戏剧性,但你在面试中做的非常好,惊艳了所有人并获得了高薪工作。你回家告诉你的邻居你的新工作并表达你激动的心情。你的朋友告诉你他也在同一个办公楼里工作。他建议你坐他的车去上班。你想这不错。所以去阿里上班的第一天,你走到你邻居的房子,在他的房子前停好你的车。你等了他10分钟,但你的邻居没有出现。你然后继续开自己的车去上班,这样你不会在第一天就迟到。这就是TIMED_WAITING.用技术术语来解释,你是线程T1而你的邻居是线程T2。你释放了锁(这里是停止开车)并等了足足10分钟。如果你的邻居T2没有来,你继续开车(老司机注意车速,其他乘客记得买票)。小贴士:当线程调用以下方法时会进入TIMED_WAITING状态1,Thread.sleep 方法。2,指定超时值的 Object.wait 方法。3,指定超时值的 Thread.join 方法。4,LockSupport.parkNanos。5,LockSupport.parkUntil。
(6)TERMINATED
线程处于终止状态。同样我们在使用jstack进行线程dump的时候也很少看到该状态的线程栈。

状态小结:

这些状态中NEW状态是开始,TERMINATED是销毁,在整个线程对象的运行过程中,这个两个状态只能出现一次。其他任何状态都可以出现多次,彼此之间可以相互转换

  • 处于timed_waiting/waiting状态的线程一定不消耗cpu,处于runnable状态的线程不一定会消耗cpu,要结合当前线程代码的性质判断,是否消耗cpu

  • 如果是纯java运算代码,则消耗cpu

  • 如果线程处于网络io,很少消耗cpu

  • 如果是本地代码,通过查看代码,可以通过pstack获取本地的线程堆栈,如果是纯运算代码,则消耗cpu,如果被挂起,则不消耗,如果是io,则不怎么消耗cpu

以下是状态转化图,可以较为清晰地看到状态转换的场景与条件:

线程锁解读

线程栈中包含直接信息为:线程个数,每个线程调用的方法堆栈,当前锁的状态。从线程个数可以直接数出来,线程调用的方法堆栈,从下向上看,表示了当前线程调用哪个类哪个方法,锁的状态看起来需要一些技巧,与锁相关的重要信息如下:

  • 当一个线程占有一个锁的时候,线程堆栈会打印一个-locked<0x22bffb60>
  • 当一个线程正在等在其他线程释放该锁,线程堆栈会打印一个-waiting to lock<0x22bffb60>
  • 当一个线程占有一个锁,但又执行在该锁的wait上,线程堆栈中首先打印locked,然后打印-waiting on <0x22c03c60>

在线程堆栈中与锁相关的三个最重要的特征字:locked,waiting to lock,waiting on 了解这三个特征字,就可以对锁进行分析了。

一般情况下,当一个或一些线程正在等待一个锁的时候,应该有一个线程占用了这个锁,即如果有一个线程正在等待一个锁,该锁必然被另一个线程占用,从线程堆栈中看,如果看到waiting to lock<0x22bffb60>,应该也应该有locked<0x22bffb60>,大多数情况下确实如此,但是有些情况下,会发现线程堆栈中可能根本没有locked<0x22bffb60>,而只有waiting to ,这是什么原因呢,实际上,在一个线程释放锁和另一个线程被唤醒之间有一个时间窗,如果这个期间,恰好打印堆栈信息,那么只会找到waiting to ,但是找不到locked 该锁的线程,当然不同的JAVA虚拟机有不同的实现策略,不一定会立刻响应请求,也许会等待正在执行的线程执行完成。

java线程堆栈信息分析相关推荐

  1. linux查看java堆栈信息_Java运行状态分析2:获取线程堆栈信息

    Java运行状态分析2:获取线程堆栈信息 基本概念 出现内存泄漏或者运行缓慢场景,有时候无法直接从业务日志看出问题时候,需要分析jvm内存和线程堆栈 线程堆栈信息主要记录jvm线程在某时刻线程执行情况 ...

  2. java打印线程堆栈_Java问题定位之Java线程堆栈分析

    采用Java开发的大型应用系统越来越大,越来越复杂,很多系统集成在一起,整个系统看起来像个黑盒子.系统运行遭遇问题(系统停止响应,运行越来越慢,或者性能低下,甚至系统宕掉),如何速度命中问题的根本原因 ...

  3. jstack-查看Java进程的线程堆栈信息,锁定高消耗资源代码

    jstack主要用来查看某个Java进程内的线程堆栈信息.语法格式如下: jstack [option] pid jstack [option] executable core jstack [opt ...

  4. java线程堆栈nid.tid_java多线程死锁 Java问题定位之Java线程堆栈分析(2)

    从上面的main线程看,线程堆栈里面的最直观的信息是当前线程的调用上下文,即从哪个函数调用到哪个函数(从下往上看),正执行到哪一类的哪一行,借助这些信息,我们就对当前系统正在做什么一目了然. 另外,从 ...

  5. jpa,分析duid参数,当前用户的最大线程数,线上问题排查,stack命令查看占用CPU高的线程堆栈信息

    1.先查看应用进程号: ps -ef | grep 应用名 ,也就是 pid 2.查看pid垃圾回收情况: jstat -gc pid 5000(时间间隔) 3.dump jvm二进制的内存详细使用情 ...

  6. java线程堆栈_深入JVM剖析Java的线程堆栈

    在这篇文章里我将教会你如何分析JVM的线程堆栈以及如何从堆栈信息中找出问题的根因.在我看来线程堆栈分析技术是Java EE产品支持工程师所必须掌握的一门技术.在线程堆栈中存储的信息,通常远超出你的想象 ...

  7. java线程堆栈nid.tid_java排查一个线上死循环cpu暴涨的过程分析

    问题,打一个页面cpu暴涨,打开一次就涨100%,一会系统就卡的不行了. 排查方法,因为是线上的linux,没有用jvm监控工具rim链接上去. 只好用命令排查: top cpu排序,一个java进程 ...

  8. java visualvm 内存_【Java线程与内存分析工具】VisualVM与MAT简明教程

    前言 本文将简要介绍Java线程与内存分析工具VisualVM和MAT的使用,进一步的学习可参考官网或工具帮助(例如MAT:Help -> Welcome -> Tutorials),并在 ...

  9. Win Linux Java 项目堆栈信息调试教程

    使用堆栈信息排查系统性能问题,多线程程序调优(因为这类问题往往都不会输出日志或日志输出定位代码排查的范围太大),堆栈信息对这类问题定位速度快而精准. 查询堆栈信息全文涉及到的指令 线程状态相关 常见到 ...

最新文章

  1. 成功解决h5py\_init_.py:26:FutureWarning: Conversion of the second argument of issubdtype from `float` to
  2. 程序员日记我们需要有条理的生活
  3. php分块查找,索引查找(索引查找、分块查找) C语言实现
  4. 拳王虚拟项目公社:知识付费之出售虚拟教程资源赚钱,小白轻松赚钱的方式
  5. c++中vector用法(涵盖算法题中知识点)
  6. 【月径流预测】基于matlab未来搜索算法算法优化BP神经网络月径流预测【含Matlab源码 2001期】
  7. App Store、Steam、Google Play等出海应用、游戏如何收款到国内账户
  8. 这家曾经的开源明星企业竟然生死未卜了
  9. 【JS】Unicode编码
  10. macOS安装brew和ffmpeg下载m3u8
  11. 单摄像机对于二维平面的测量
  12. 商标设计需要注意的要素
  13. 简单学JAVA-学好Java能做啥
  14. 捕鱼里金币掉落的效果
  15. html ui标记是什么,ui是啥
  16. 服务器网站崩溃怎么解决
  17. web3探索,从密码朋克开始的奇妙故事
  18. 笔记本写代码 屏幕尺寸_为什么笔记本电脑的屏幕尺寸如此奇怪?
  19. Java 编程技巧之样板代码
  20. 一起去追寻那大漠孤烟

热门文章

  1. css表格中下拉菜单怎么设置,css如何实现下拉菜单?css实现下拉菜单的方法(完整代码)...
  2. centos 6.5 编译php mysql5.6_CentOS 6.5编译安装Nginx+MySQL+PHP
  3. Android 操作系统 获取Root权限 原理解析
  4. 一个好的网站logo设计长这样
  5. 微型计算机可以用于办公嘛,两千元出头性能也不差,家用办公配置这样配最省钱...
  6. 职称申报时如何选择职称评委会?
  7. 全球及中国制造执行系统(MES)软件行业未来投资前景预测报告2022-2027年
  8. 使用java Graphics2D生成海报图片
  9. python利用bs4爬取外国高清图片网站
  10. 苹果android什么意思,从苹果换到安卓是什么体验?不妨看看这些iPhone用户的说法...