应用程序在运行过程中,除了会遇到bug和执行结果不符合我们的预期外,有时候应用程序的响应也会出现问题。比如同一个页面请求,正常情况下,两秒内返回结果,页面已经渲染显示完毕。不正常的时候会看到页面请求一直在加载,但是没有收到响应。当然,此时可以分析的点有很多:操作系统可能占用较多的CPU、内存等资源;也可能是应用程序中有很多线程在等待;在涉及读数据库等操作时,可能会遇到锁的问题。这次忽略操作系统层面的东西,只分析应用中可能遇到的情况。回到上面提到的那个问题,正常情况是两秒就返回了,但是这次一分钟就没有返回了。这时候如果关注应用,就需要分析它的线程执行情况,了解定位的具体问题。这时候,本次要说的“武器”jstack就可以快速定位,直达受影响区域。什么是jstac?这是OracleJDK中默认包含的一个工具,用于打印正在执行的Java进程的当前线程堆栈信息。官方介绍如下:jstack为给定的Java进程或核心文件或远程调试服务器打印Java线程的Java堆栈跟踪。对于每个Java框架,打印完整的类名、方法名、'bci'(字节代码索引)和行号(如果有)。注意几个关键点:每个JavaFrame的完整类名和方法名,如果能获取到行号,就会显示行号。看过上一篇介绍调试技巧(80%程序员都不懂的调试技巧)的朋友可能还记得,其中一个功能就是DropFrame实现反向执行。而这里是一个地方,都对应线程中的一级调用。使用jstack打印出的信息与一般应用程序遇到异常时的printStackTrace基本相同,只是只是一个线程调用链。在这里,通过工具,可以打印出应用程序中的所有线程。Usage使用方法与一般的Java分析工具类似,使用命令名<可选参数>+pid(进程id)的格式。比如jstack,一般可以直接把pid应用到jstack上。这里的pid可以通过Java的jps工具,Linux下的ps工具,Windows下的任务管理器获取。输出我们以一个Tomcat进程为例,输出类似这样:我们看上面的几个方框:左上角是当前线程的线程名,我们可以通过它在其中找到我们关心的线程应用程序中的大量线程。要执行的操作。比如在Tomcat中,http-port-x等线程一般都是请求处理线程。当页面响应慢时,可以直接找这类线程。随着请求数量的增加,线程数量也会增加。所以在一般的多线程应用开发中,一个比较实用的建议就是给创建的线程起一个有意义的名字,不然typedstack里面会出现一大堆thread-1和thread-2,天知道哪个是你的。第二行方框中的内容表示当前线程的执行状态,是运行中,TIME_WAITING,还是等待锁等,可以根据线程状态来理解。第三个大框里的内容和我们得到异常时stackTrace输出的一样,就是当前代码的调用链。第四个框的内容是当前线程的锁状态。上面的截图是没有锁相互占用时的输出。如果在多线程中有锁在等待,会有类似这样的输出:注意此时线程状态已经变为BLOCKED,同时在线程的调用链中,有一个等待锁定的输出。在护锁的线程中,会有一个锁xxx,就是当前的锁对象。通过这个,你可以看到当前有哪些线程在等待同一个锁。那么回到上一个问题,如果此时是锁占用导致的,从输出可以看出来。同时,如果数据库连接池满了,线程会停止在数据库连接的操作上,这一点在stackTrace中一目了然,甚至等待网络Socket读取也会在调用链中体现出来,所以以便快速定位并解决问题。对于应用程序的观察分析,需要注意的是可以每隔几秒执行一次jstack,分析输出结果,然后比较几个输出结果的差异,看看这段时间应用程序执行到哪里了.和解决问题。【本文为专栏作家“侯书城”原创稿件,转载请通过作者微信公众号“Tomcat物语”获得授权】点此查看本作者更多好文
