原来jdk自带了这么好玩的工具使用jstack定位死循环
什么是jstackjstack的主要作用是查看或者导出java线程的堆栈信息(快照);用于堆栈跟踪,当我们使用jstack命令时,它会将指定进程内的所有线程中方法的调用栈打印出来。
线程快照是java虚拟机内每一个线程正在执行的方法堆栈的集合,生成线程快照的主要目的是用于定位线程出现问题的位置;常见的问题有 响应时间长 线程死锁 死循环
当线程停顿的时候通过jstack来查看各个线程的调用堆栈,就可以知道线程的执行过程中在后台做了哪些事,在等待哪些资源造成的卡顿。 使用方法
命令格式 jstack [options] options 参数说明
jstack的option参数并不多,真正用到的也就三个,接下来我们一个个介绍一下
- -F :当线程挂起(Suspended)时,使用jstack -l pid命令是不会打印堆栈信息的,使用-F则可以强制输出线程堆栈;但是会停止但
--l :打印的信息除了堆栈外,还会显示锁的附加信息;
--m :同时输出java和C/C++的堆栈信息;在java的系统类库里面,有很多方法都是native修饰的,这些native修饰的方法你在java层面是看不到源码的,因为这些方法都是C/C++实现的;状态说明
在线程的堆栈中,需要特别留意以下几种状态: Deadlock: 死锁(重点关注) Waiting on condition: 等待资源(重点关注) Waiting on monitor entry : 等待获取监视器(重点关注) Blocked :阻塞(重点关注) Runnable :执行中 Suspended : 暂停 Object.wait()或 TIME_WAITING :对象等待中 Parked : 停止 no option 参数
不带option参数的命令 jstack 12771
打印结果如下 # ......忽略其他堆栈信息我们只关注main线程的堆栈 "main" #1 prio=5 os_prio=31 tid=0x00007fee9101a000 nid=0x2903 runnable [0x00007000085aa000] java.lang.Thread.State: RUNNABLE at java.io.FileInputStream.readBytes(Native Method) at java.io.FileInputStream.read(FileInputStream.java:255) at java.io.BufferedInputStream.fill(BufferedInputStream.java:246) at java.io.BufferedInputStream.read(BufferedInputStream.java:265) - locked <0x000000076ab1ead0> (a java.io.BufferedInputStream) at com.test.Test.main(Test.java:12) # .......忽略其他堆栈信息我们只关注main线程的堆栈
第一行各个单词的解析, "main":线程名称 。 prio:线程优先级 tid:指Java Thread id。 nid:指native线程的id… [0x00007000085aa000]:线程栈起始地址。 -l打印锁的附加信息
这里我们使用2个窗口,分别使用以下2个命令来测试 # 第一个窗口执行 jstack -l 12771 # 第二个窗口中执行 jstack 12771
通过2个窗口对比可以看到,加了-l的命令多打印了锁的信息;
导出堆栈文件
一般情况下,如果程序出错了, 都不会直接在生产环境的服务器上找错误,这个时候就可以用到一个非常实用的功能,将堆栈快照导出来,然后copy到别的电脑上看,命令如下 jstack -l 2289 > jstackDump.txt
执行后,就可以看到文件已经导出来了
通过cat命令可以看到,里面的内容和我们在命令行输出的内容是一样的
实战一 、 找出cpu占用最高的线程(linux系统)
首先我们准备好一个死循环的线程,在线程内定一个while的死循环,并且给这个线程起个名字为:yexindogn,阿里巴巴的开发规范里面有一个规定,就是每个线程必须起一个名字,起名字就是为了 以后程序出问题的时候好找错误; public static void main(String[] args) throws InterruptedException { new Thread(new Runnable() { @Override public void run() { while(true){ System.out.println(112); } } },"yexindong").start(); System.out.println("我执行了"); }
接着我们将此代码打成jar包扔到linux服务器上运行,直接输入 java -jar Test.jar 命令即可运行,运行后我们可以看到控制台一直在输出112这个字符,这就代表程序已经在运行了;
接着在看下CPU的运行情况,使用top命令查看cpu占用情况,排在第一位的是进程号为30328的进程,占用了6.6%的cpu; 这边我使用了2个命令行连到同一台服务器,一个窗口用来运行刚刚的jar包,另一个窗口用来查找错误;
找线程 - 第一种方式
知道进程号了,接着就是找线程了,输入以下命令 top -Hp 30328
打印结果如下,这里有一点需要注意,在我们加上-Hp指令后,PID展示就是线程的id了,这时候我们看到占用CPU最高的线程id是30365;
找线程 - 第二种方式
还有另一种方式,就是使用ps命令来查找线程 ps -mp 30328 -o THREAD,tid,time| sort -n -k1 -r
通过这个命令我们可以看到这边占用最高的线程id也是30365 ;
以上的方式我们成功找到了占用cpu高的线程id是30365,但这个id是十进制的,在这里需要先转为16进制,输入命令 printf "%x " 30365
计算出对应的16进制为:769d
当然也可以用其他的计算工具,比如mac系统自带计算器就支持进制之间的转换
使用jstack分析堆栈快照1、快速查找 (推荐使用)
在命令行输入以下命令,这种方法更加快速,推荐使用 jstack 12771 | grep -A 20 "769d"
其中,grep 命令是查找结果为769d的内容,-A 20 表示打印匹配所在行的后20行内容。直接帮我们定位到所在线程的堆栈,结果如下 "yexindong" #8 prio=5 os_prio=0 tid=0x00007effd0182000 nid=0x769d runnable [0x00007effbea0f000] java.lang.Thread.State: RUNNABLE at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:326) at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) - locked <0x00000000ecd6ea10> (a java.io.BufferedOutputStream) at java.io.PrintStream.write(PrintStream.java:482) - locked <0x00000000ecd65a10> (a java.io.PrintStream) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221) at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291) at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104) - locked <0x00000000ecd659c8> (a java.io.OutputStreamWriter) at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185) at java.io.PrintStream.newLine(PrintStream.java:546) - locked <0x00000000ecd65a10> (a java.io.PrintStream) at java.io.PrintStream.println(PrintStream.java:737) - locked <0x00000000ecd65a10> (a java.io.PrintStream) at com.test.Test$1.run(Test.java:13) at java.lang.Thread.run(Thread.java:748) 2、常规方法
当然也可以用下面的死办法,先打印出所有的堆栈快照; jstack 30328
打印结果如下 [root@VM_0_5_centos ~]# jstack 30328 2021-07-14 23:40:34 Full thread dump OpenJDK 64-Bit Server VM (25.232-b09 mixed mode): "Attach Listener" #10 daemon prio=9 os_prio=0 tid=0x00007effa4001000 nid=0x12f9 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "DestroyJavaVM" #9 prio=5 os_prio=0 tid=0x00007effd004b800 nid=0x7679 waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "yexindong" #8 prio=5 os_prio=0 tid=0x00007effd0182000 nid=0x769d runnable [0x00007effbea0f000] java.lang.Thread.State: RUNNABLE at java.io.FileOutputStream.writeBytes(Native Method) at java.io.FileOutputStream.write(FileOutputStream.java:326) at java.io.BufferedOutputStream.flushBuffer(BufferedOutputStream.java:82) at java.io.BufferedOutputStream.flush(BufferedOutputStream.java:140) - locked <0x00000000ecd6ea10> (a java.io.BufferedOutputStream) at java.io.PrintStream.write(PrintStream.java:482) - locked <0x00000000ecd65a10> (a java.io.PrintStream) at sun.nio.cs.StreamEncoder.writeBytes(StreamEncoder.java:221) at sun.nio.cs.StreamEncoder.implFlushBuffer(StreamEncoder.java:291) at sun.nio.cs.StreamEncoder.flushBuffer(StreamEncoder.java:104) - locked <0x00000000ecd659c8> (a java.io.OutputStreamWriter) at java.io.OutputStreamWriter.flushBuffer(OutputStreamWriter.java:185) at java.io.PrintStream.newLine(PrintStream.java:546) - locked <0x00000000ecd65a10> (a java.io.PrintStream) at java.io.PrintStream.println(PrintStream.java:737) - locked <0x00000000ecd65a10> (a java.io.PrintStream) at com.test.Test$1.run(Test.java:13) at java.lang.Thread.run(Thread.java:748) "Service Thread" #7 daemon prio=9 os_prio=0 tid=0x00007effd013e800 nid=0x769b runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C1 CompilerThread1" #6 daemon prio=9 os_prio=0 tid=0x00007effd013b800 nid=0x769a waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "C2 CompilerThread0" #5 daemon prio=9 os_prio=0 tid=0x00007effd012d000 nid=0x768b waiting on condition [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Signal Dispatcher" #4 daemon prio=9 os_prio=0 tid=0x00007effd012a800 nid=0x7689 runnable [0x0000000000000000] java.lang.Thread.State: RUNNABLE "Finalizer" #3 daemon prio=8 os_prio=0 tid=0x00007effd0101000 nid=0x7683 in Object.wait() [0x00007effbf906000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000ecd66260> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) - locked <0x00000000ecd66260> (a java.lang.ref.ReferenceQueue$Lock) at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216) "Reference Handler" #2 daemon prio=10 os_prio=0 tid=0x00007effd00fc000 nid=0x767f in Object.wait() [0x00007effbfa07000] java.lang.Thread.State: WAITING (on object monitor) at java.lang.Object.wait(Native Method) - waiting on <0x00000000ecd66418> (a java.lang.ref.Reference$Lock) at java.lang.Object.wait(Object.java:502) at java.lang.ref.Reference.tryHandlePending(Reference.java:191) - locked <0x00000000ecd66418> (a java.lang.ref.Reference$Lock) at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) "VM Thread" os_prio=0 tid=0x00007effd00f2800 nid=0x767d runnable "VM Periodic Task Thread" os_prio=0 tid=0x00007effd0141000 nid=0x769c waiting on condition JNI global references: 5
接着我用刚刚计算出来的16进制复制出来在这里搜索一下,经过查看就知道是我们刚刚起了名字为yexindong的线程出错了,出错的位置在Test.java的第13行代码
我们看看java代码,确实是第13行这里的死循环导致的
实战二、 找出cpu占用最高的线程(windows系统)
首先打开任务管理器,因为默认windows的任务管理器是不显示进程pid的,所以我们需要设置一下,选择 查看 选择列(S)...
选中PID进程表示符后点击确定按钮
查进程号pid
然后我们就可以看到占用CPU最高的java进程PID为:976
查线程号TID
因为windows不能直接查看java进程中的线程信息,所以我们需要借助一个工具,这个工具是微软自己开发的,叫做Process Explorer ,网上很多,需要的童鞋请自行百度,打开后找到pid为976的进程右击选择 属性
在弹出的窗口中找到线程这一栏,它的排序默认就是按照cpu占用的率倒序排列的,所以最上面的就是占用cpu最高的线程了,记住它的线程id:3548
线程id转16进制
刚刚拿到的进程id是十进制的,但是我们导出的jstack信息里面,线程id是以16进制来展示的,所以我们要先将这个线程id为3548转为16进制的,使用windows自带的计算器即可,在快捷命令行输入calc
计算器打开后将其设置为程序员使用的计算器
接着输入线程id3548,在按一下16进制,就会自动进行转换,结果为ddc,记住这个16进制;
使用jstack导出堆栈并分析
在命令行输入以下指令导出进程的堆栈快照信息 jstack 976 > jstackInfo.txt
几秒钟后,快照导出了,静静地躺在文件夹里,等待着我们打开
用Notepad++打开导出的文件,搜索刚刚计算出来的16进制ddc,就可以定位到线程出错的位置了
在java层面打印堆栈
有些童鞋可能会觉得用这个jstack命令麻烦了,那java在代码里面可不可以打印出堆栈呢?你别说,还真有,就是这个方法:Thread.getAllStackTraces();光说不练假把式,来个demo测试一下吧 public static void main(String[] args) throws InterruptedException { // 第一个线程,死循环 new Thread(new Runnable() { @Override public void run() { while (true){ } } },"while").start(); // 延时一秒 Thread.sleep(1000); // 第二个线程,用来打印堆栈 new Thread(new Runnable() { @Override public void run() { Map allStackTraces = Thread.getAllStackTraces(); for (Map.Entry threadEntry : allStackTraces.entrySet()) { Thread key = threadEntry.getKey(); System.out.println(key); StackTraceElement[] value = threadEntry.getValue(); for (StackTraceElement stackTraceElement : value) { System.out.println(" "+stackTraceElement.toString()); } } } },"stack-info").start(); }
执行后打印结果如下,由此可以看到,将当前进程的所有线程都打印出来了,但是这边只打印了简单的堆栈信息,对于开发人员来说,已经起到了监控作用; Thread[Reference Handler,10,system] java.lang.Object.wait(Native Method) java.lang.Object.wait(Object.java:502) java.lang.ref.Reference.tryHandlePending(Reference.java:191) java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153) Thread[while,5,main] com.test.Test$1.run(Test.java:29) java.lang.Thread.run(Thread.java:748) Thread[Monitor Ctrl-Break,5,main] java.net.SocketInputStream.socketRead0(Native Method) java.net.SocketInputStream.socketRead(SocketInputStream.java:116) java.net.SocketInputStream.read(SocketInputStream.java:171) java.net.SocketInputStream.read(SocketInputStream.java:141) sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284) sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326) sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178) java.io.InputStreamReader.read(InputStreamReader.java:184) java.io.BufferedReader.fill(BufferedReader.java:161) java.io.BufferedReader.readLine(BufferedReader.java:324) java.io.BufferedReader.readLine(BufferedReader.java:389) com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:61) Thread[Finalizer,8,system] java.lang.Object.wait(Native Method) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144) java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165) java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216) Thread[stack-info,5,main] java.lang.Thread.dumpThreads(Native Method) java.lang.Thread.getAllStackTraces(Thread.java:1610) com.test.Test$2.run(Test.java:39) java.lang.Thread.run(Thread.java:748) Thread[Attach Listener,9,system] Thread[Signal Dispatcher,9,system] 完
作为调优和找错的工具来说,可以说jstack是用的最多的一个工具了,但是由于局限性,现在已经慢慢被替换掉了;大家更倾向于使用阿里巴巴开发的工具arthas;感兴趣的童鞋可以了解下!
于无声处听惊雷!华为或许已经自主研发5G射频芯片4G手机卖9千!这大概是对华为最近发布的手机评论最多的内容了。由于华为5G射频芯片的封锁,导致华为至今不能再推出5G手机,销量也一落千丈。华为也是靠着新机的不断发布来保持其网络热度
买不起?放不下?98寸巨屏电视看着是什么感觉?前言岁月如梭。谁曾想到,95年的一万块钱只能买一台29寸的大彩电?就这价格都供不应求在那些过去的时光里,一台29寸的电视承载着我们对于影像的所有想象。但科技的发展从未止步,技术的前
音箱科普系列音箱是一套音响系统的灵魂,对系统声音起决定性的作用。许多音响入门爱好者在选择音箱时都会很迷茫,不知道该如何选择。本文希望用尽可能详尽地讲解音箱市面上的音箱类型,以及其主要的声音特点
骁龙芯透明后盖!一加联合创始人官宣Nothing手机来了关注数码圈的各位应该听说过这个消息,一加的联合创始人裴宇将会入局智能手机市场,而就在近日,这位大佬正式宣布,新创立的Nothing品牌将推出自家的智能手机NothingPhone(
放弃苹果买这3款,只因性能太强,价格还低,买错实在可惜众所周知,如果选一款手机来代表高端序列,毫无疑问,是iPhone手机,凭借着过硬的实力,硬生生只靠高端旗舰机就能在日趋激烈的竞争环境下取得前三的成绩,但是吧,iPhone手机也有自
红魔72月17日召开发布会,搭载18GB超大内存目前红魔7的发布会时间已经被正式确定下来,将会在本月的17日与大家正式见面,而且这款手机也是今年的第一款游戏手机,所以还是非常值得期待的。据悉,红魔7系列是全球第一款搭载骁龙8的游
数字人民币为什么能让美国害怕?这次俄罗斯被踢出SWIFT系统,说是欧美在金融领域扔下了一枚核弹,如果说SWIFT是原子弹的话,那数字人民币就是氢弹。数字人民币有三个特点流通,标记,离线。流通数字人民币对应的是M
剑指谷歌苹果等美国科技巨头,欧盟敲定里程碑式数字法案欧盟数字经济新规的进度条再次向前推进,在欧洲经营的美国科技巨头压力倍增。24日,欧盟谈判代表敲定数字市场法最终细节,为这项重磅法案在明年生效奠定基础。该法案将给在欧洲经营的科技巨头
回顾美国联邦通信委员会(FCC)的黑名单2021年3月12日美国联邦通信委员会(简称FCC)公共安全部和国土安全局宣布,根据2019年安全和可信通信网络法案,将5家中国企业的产品和服务列入对美国国家安全构成威胁的通信设备
用墨水平板来玩游戏是怎样的一种体验?华为MatePadPaper体验华为要进军电子书阅读器行业了?是,但也不全是华为在最近为自家MatePad推出全新产品线,名为MatePadPaper,有着一块10。3英寸的墨水屏,却不是普通的电子书阅读器,也不
周末行业观点汇总作者格隆汇研究数据支持勾股大数据(www。gogudata。com)新能源汽车短中期关注点a)风险点零部件公司的业绩从爱柯迪,旭升股份以及三花智控三家来看,零部件大概率4季度利润承
专访东莞现代精工董事长何建明为客户提供全方位技术解决方案2021年是十四五开局之年新征程开启之年。站在新起点上,如何迈好第一步走好新征程至关重要。2021年,是挑战亦或是机遇?于产业,于企业,又该有一个怎样的应对方法论?面对这些问题,我
理想汽车8月12日港股上市发行价最高为每股150港元理想汽车在港交所公告,理想汽车将于2021年8月12日(星期四)上午九时开始在联交所买卖将通过香港IPO发行1亿股股票,最高发行价为每股150港元,可最多筹集150亿港元(合124
上半年98。8万辆新能源车都卖到了哪儿?文章来源盖世汽车冯凉爽今年上半年,新能源汽车再次迎来高速发展态势。盖世汽车根据保监会提供的上险数整理得出,上半年我国新能源汽车累计销量为98。8万辆,同比激增222。其中,纯电动汽
OPPO也膨胀到开始造车了吗?今年年初,OPPO对外就公布了多项汽车领域的专利,而在昨天的6月29日,根据天眼查App显示,OPPO广东移动通信有限公司公开车辆控制方法和装置电子设备计算机可读存储介质专利,公开
宁德时代全球第一?我要的是引领时代据韩联社消息,韩国能源市场分析公司SNEResearch发布的调查结果显示,今年上半年中国电池巨头宁德时代(CATL)全球动力电池装机量达到34。1GWh,同比增长234。2,排名
零跑汽车获新一轮45亿融资,杭州国资出资额占6成8月18日,据零跑汽车发布消息称,该公司已经完成新一轮融资,融资金额45亿元。本轮战略投资由中金资本领投,杭州国资中信建投和中信戴卡等一同入股。值得注意的是,在零跑汽车此轮融资中,
吉利2021上半年财报公布营收450亿发布五年计划8月18日,吉利汽车控股有限公司(简称吉利汽车)(HK。0175)发布2021年上半年财报,财报数据如下图片来源网络2021年上半年,吉利汽车营收达450亿人民币,同比增长22扣除
特斯拉自动驾驶虚假宣传被彻查在蔚来汽车因为国内致命车祸深陷舆论危机之际,自动驾驶的始作俑者特斯拉也遇到了麻烦。据外媒报道,两名美国参议员已经敦促联邦贸易委员会调查特斯拉,认为特斯拉涉嫌虚假宣传和误导消费者,将
盘点国内积极布局车载中控的TierOne企业(23家)图文来源亚洲新能源汽车网微信公众号随着汽车智能系统的普及,智能汽车内的物理按键逐渐被触控按键取代,车载显示呈现大屏化多屏化高清化等发展趋势,触控可能成为车载显示的标配。而一块智能的
让智能公路控制汽车,实现智能驾驶?智能驾驶与智能公路智能驾驶即自动驾驶,该技术依靠人工智能视觉计算雷达监控装置以及全球定位系统,实现汽车自动驾驶车辆。现在自动驾驶的特点是让汽车去适应道路,应对复杂的路况。智能公路又
专访展迅总经理袁应棠严密精准,保证产品的高质量2021年是十四五开局之年新征程开启之年。站在新起点上,如何迈好第一步走好新征程至关重要。于新能源汽车产业,于车载显示的相关企业,应该如何面对2021年的挑战和机遇?围绕新能源汽车