在大型的应用中,线程堆栈打印出来特别多,如何从众多的信息中找到真正有用,有价值的信息,我们需要一定的技巧。本文对此详细介绍。

我们可以从三个方面分析:堆栈的局部信息,一次堆栈的统计信息,多个堆栈的对比信息。

  • 从一次的堆栈信息中,我们可以直接获取以下信息:

    • 每一个线程的调用关系,当前线程在调用哪些函数
    • 每个线程的当前状态,持有哪些锁,在等待哪些锁?
  • 从一次堆栈信息中,我们还可以统计以下信息:
    • 是否有很多线程都在等待同一个锁,说明这个系统存在性能瓶颈,导致了锁竞争
    • 当前线程的总数量
    • 大多数线程在干什么,在执行什么代码?
  • 从多次的堆栈信息中,可以得到以下信息:
    • 一个线程是否长期执行,如果每次打印堆栈某个线程一直处于同样的调用上下文中,那么说明这个线程一直执行这段代码,此时要根据代码逻辑检查,是否合理。
    • 某个线程是否长期存在获取不到锁的情况,线程是否永远得不到唤醒,如果某一个线程一直等在一个锁,就要检查占用这个锁的线程为什么不释放。

如果说打印一次线程堆栈是平面,那么打印多次就是立体了,我们可以看到一段运行的情况。

接下来从以下几个方面分析:

  • 线程死锁分析
  • java代码导致的cpu过高分析
  • 死循环分析
  • 资源不足分析性能瓶颈分析

线程死锁分析

线程死锁的原因:当两个或者多个线程正在等待被对方占用的锁,死锁就会发生。在时间0的时候,线程0占用lock0锁,线程1占用lock1锁。在时间1,进行了其它操作,在时间2,线程0,1企图持有对方的锁,但是由于2个锁已经在时间0被锁住,所以只能等待释放。由于这两个线程互相要等待被对方占有的锁,自己才能继续,因此就造成了死锁。
 
 
我们看以下示例:
 
从打印的堆栈信息可以看到第一行,found one java-level deadlock ,如果线程存在死锁情况,堆栈会直接给出死锁的分析结果。从堆栈信息中可以看到,TestThread2中持有锁<0x22bffb10>但在等待锁<0x22bffb08>,相反,TestThread2中持有锁<0x22bffb08>,但在等待锁<0x22bffb10>。这里说的死锁是真正意义上的死锁,由于代码的错误导致的,严重程度取决于线程执行什么性质的功能代码,如果是关键功能,可能造成整个系统的瘫痪。死锁的2个或者多个线程是不消耗cpu的,所以认为死锁会导致cpu100%是错误的。
 

Java代码死循环等导致的cpu过高的分析

当系统负载大的时候会导致cpu过高,但不正确的代码也会导致cpu过高,比如死循环。我们如何从线程堆栈中找到死循环的线程呢?方法就是多次打印堆栈信息,通过对比前后的线程,找到一致运行的线程。具体步骤如下:
  1. 通过上一篇文章介绍过的方法打印第一次堆栈信息
  2. 等待一定时间,再次打印第二次堆栈信息
  3. 预处理2次堆栈信息,首先排除等待状态的线程,这种状态的线程不消耗cpu,前面已经讲过。我们只关注runnable状态的线程。
  4. 比较前后2次预处理后的线程,找出一段时间一直活跃的线程,如果2次堆栈信息在同一个线程处于同样的调用上下文,就列为重点怀疑对象。接下来结合代码逻辑检查该线程执行的上下文所对应的代码是不是属于应该长期运行的代码。
如果通过堆栈定位没有发现可疑代码,那么cpu高可能是不恰当的内存设置导致的频繁gc,从而导致的cpu过高。
 

资源不足等导致的性能下降分析

这里说的资源包括数据库连接。大多数时候资源不足和性能瓶颈是同一类问题。当资源不足,就会导致资源的竞争,请求该资源的线程会被阻塞或者挂起,自然就导致性能下降。系统对于资源,一般的设计模式是:当需要资源的时候,获取资源,当不需要的时候就把资源释放掉。如果暂时没有可用的资源,就等待在哪里。如果有别的线程释放资源,那么等待的线程被notify,等待的线程获得资源继续运行。一般的资源设计都遵循wait/notify模式。如果资源不足,那么有大量的线程等待资源,打印的线程堆栈如果具有这个特征,说明该资源存在瓶颈。
  1. 大量的线程停在同样的调用上下文上。
  2. 资源数量配置太小,如数据库连接数,如果系统压力过大,资源不足导致线程不能及时获得资源而等待在那里。
  3. 获得资源的线程把持资源时间太久,导致资源不足。比如把一段和操作数据库无关的代码放在获取数据库连接和释放数据库连接之间。
  4. 设计不合理导致的资源占用太久,比如sql没有加索引导致执行sql太慢。
  5. 资源用完后,没有关闭导致资源泄漏或者减少。
资源不足往往表现出一个现象是系统越来越慢,并最终停止响应,超时。
 

多个锁导致的锁链分析

很多线程在等待不同的锁,有的锁竞争可能是由于另一锁对象竞争导致,这时候需要找到根源。
 
 
 
  1. 看到有40多个线程在等待锁0xbef17078,首先找到已经占有这把锁的线程thread-196
  2. 看到thread-196占有锁,0xbef17078,但又在等待锁<0xbc7b4110>,那么此时需要再找占有<0xbc7b4110>这个锁的线程,即thread-609。
  3. 那么占有锁<0xbc7b4110>的线程是问题的根源,下一步就是查到底为什么这个线程长时间占有这个锁。可能的原因是持有这把锁的线程正在执行的代码性能比较低,导致占有时间过长。

Java问题定位之如何借助线程堆栈进行问题分析的更多相关文章

  1. 通过 Java 线程堆栈进行性能瓶颈分析

    改善性能意味着用更少的资源做更多的事情.为了利用并发来提高系统性能,我们需要更有效的利用现有的处理器资源,这意味着我们期望使 CPU 尽可能出于忙碌状态(当然,并不是让 CPU 周期出于应付无用计算, ...

  2. 通过Java 线程堆栈进行性能瓶颈分析

    改善性能意味着用更少的资源做更多的事情.为了利用并发来提高系统性能,我们需要更有效的利用现有的处理器资源,这意味着我们期望使 CPU 尽可能出于忙碌状态(当然,并不是让 CPU 周期出于应付无用计算, ...

  3. Java线程堆栈分析

    不知觉间工作已有一年了,闲下来的时候总会思考下,作为一名Java程序员,不能一直停留在开发业务使用框架上面.老话说得好,机会是留给有准备的人的,因此,开始计划看一些Java底层一点的东西,尝试开始在学 ...

  4. Java问题定位之Java线程堆栈分析

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

  5. JAVA问题定位跟踪技术

    常用的JAVA调试技巧: 线程堆栈解读 性能瓶颈分析 远程调试 内存泄露检测 常用工具集: proc工具集 系统跟踪命令truss/strace Core文件管理coreadm 进程状态监控prsta ...

  6. JVM:如何分析线程堆栈

    英文原文:JVM: How to analyze Thread Dump 在这篇文章里我将教会你如何分析JVM的线程堆栈以及如何从堆栈信息中找出问题的根因.在我看来线程堆栈分析技术是Java EE产品 ...

  7. openjdk-alpine镜像无法打印线程堆栈和内存堆栈问题

    基于openjdk:8u171-alpine构建的java镜像,使用jstack命令打印线程的时候会提示以下错误: /opt # ps -ef PID USER TIME COMMAND 1 root ...

  8. Java项目性能瓶颈分析及定位(八)——Java线程堆栈分析(五)

    对于CPU而言,常见的瓶颈主要有两种:服务器的压力很小,但是CPU的利用率却很高,这样的性能瓶颈相对比较容易定位(好比我只是说了你一句,你就哭了,你的弱点立马就暴露出来了):给服务器施加的压力很大,但 ...

  9. 定位java程序中占用cpu最高的线程堆栈信息

    找出占用cpu最高的线程堆栈信息 在java编码中,有时会因为粗心导致cpu占用较高的情况,为了避免影响程序的正常运行,需要找到问题并解决.这里模拟一个cpu占用较高的场景,并尝试定位到代码行. 示例 ...

随机推荐

  1. 在docker容器中安装vim命令进行编辑文件

    首先执行: 执行apt-get update, 然后再次执行apt-get install vim即可成功安装vim. 然后我们就可以使用vim编辑 如果不进行更新就会报错: 此时会报出bash: v ...

  2. Vue-Router路由Vue-CLI脚手架和模块化开发 之 路由的动态跳转

    在上一篇的博文中,实现的跳转是在页面中进行实现的 利用vue-router实例方法,使用js对路由进行动态跳转: 1.router.push:参数为路由对象,跳转到指定路由,跳转后会产生历史记录: & ...

  3. 【Leetcode】Count and Say

    The count-and-say sequence is the sequence of integers beginning as follows:1, 11, 21, 1211, 111221, ...

  4. C# web项目添加*.ashx文件后报错处理

    说明:我是菜鸟,博文水平有限,仅作学习过程中的备忘笔记 1.截图信息: ———————————————————————————————————————————————————————————————— ...

  5. 2016"百度之星" - 资格赛(Astar Round1)D

    Problem Description 度熊所居住的 D 国,是一个完全尊重人权的国度.以至于这个国家的所有人命名自己的名字都非常奇怪.一个人的名字由若干个字符组成,同样的,这些字符的全排列的结果中的 ...

  6. delete ELK index

    Go to tab “Dev Tools”4. On the left console type:GET _cat/indices?v&s=store.size:descand execute ...

  7. linux安装php7

    之前一直对linux研究的比较少,终于下定决心好好把linux玩一下 首先~我是安装了vm虚拟机,然后使用的是centos7的版本.因为vm不好复制粘贴,故使用了xshell连接了我的linux进行操 ...

  8. sql的几种常用锁简述

    比较全的文章地址保存下:http://www.cnblogs.com/knowledgesea/p/3714417.html SELECT * FROM dbo.AASELECT * FROM dbo ...

  9. vue父子组件通信(prop)

    先定义子组件,注册prop接收父组件传递的值 <template> <div> <div>{{message}}(子组件)</div> </div ...

  10. 使用vue-router beforEach实现判断用户登录跳转路由筛选功能

    在开发webApp的时候,考虑到用户体验,经常会把不需要调用个人数据的页面设置成游客可以访问,而当用户进入到一些需要个人数据的,例如购物车,个人中心,我的钱包等等,在进行登录的验证判断,如果判断已经登 ...