当前位置: 首页 > 后端技术 > Java

JAVA排错工具的使用(一)

时间:2023-04-02 10:27:05 Java

我们最开始接触的排错方法是DEBUG和异常报错信息,已经能够解决我们开发过程中的大部分情况。但是有时候问题并不是我们代码的问题,而定位这些问题就需要借助jvm提供的工具。排查工具jstack引入jstack命令工具获取线程堆栈信息,方便分析。有什么用?可以检测死锁分析线程的状态,观察哪里有阻塞。使用方法的预知jstack中线程的状态:NEW(新状态)新创建了一个线程对象。RUNNABLE(运行状态)线程对象创建后,其他线程调用该对象的start()方法。分为就绪状态和运行状态BLOCKED(阻塞状态)阻塞状态是线程由于某种原因放弃了CPU的使用权,暂时停止运行。直到线程进入就绪状态,才有机会进入运行状态。WAITING(等待状态)处于等待状态的线程意味着需要等待其他线程的指令才能继续运行进入这个状态。与等待状态的区别:超时后,会自动进入阻塞队列,开始竞争锁。TERMINATED(终止状态)线程执行结束后的状态。jstack日志中出现的关键信息deadlock:表示出现了死锁Waitoncondition:等待某个资源或条件发生来唤醒自己。Blocked:BlockingWaitingonmonitorentry:等待获取锁(说明这个线程通过synchronized(obj){...}申请进入了临界区,从而进入下图1中的“EntrySet”队列,但是obj对应的monitor被其他线程拥有,所以这个线程在EntrySet队列中等待。)Object.wait()中:获取到锁后,执行obj.wait()放弃锁。“TIMED_WAITING(停车)”中的timed_waiting指的是等待状态,只是这里指定了时间,到达指定时间后自动退出等待状态;parking表示线程被挂起。Waitingtolock<0x00000000acf4d0c0>”表示该线程正在等待锁定地址0x00000000acf4d0c0(如果能在日志中找出谁获得了锁(如locked<0x00000000acf4d0c0>),就可以循序渐进)日志样例导入java.util.concurrent.TimeUnit;publicclassMainThread{publicstaticvoidmain(String[]args){finalThreadthread2=newThread(){publicvoidrun(){synchronized(this){try{System.out.println(Thread.currentThread());TimeUnit.SECONDS.sleep(60);}catch(InterruptedExceptione){e.printStackTrace();}}}};thread2.setName("thread2");thread2.start();synchronized(thread2){try{System.out.println(Thread.currentThread());TimeUnit.SECONDS.sleep(60);}catch(InterruptedExceptione){e.printStackTrace();}}}}"main"#1prio=5os_prio=0cpu=93.75ms经过=30.25stid=0x0000027193f7e290nid=0x4d00等待条件[0x000000fe36fff000]java.lang.Thread.State:TIMED_WAITING(睡眠)在java.lang.Thread.sleep(java.base@17/NativeMethod)在java.lang.Thread.sleep(java.base@17/Thread.java:337)在java.util.concurrent.TimeUnit.sleep(java.base@17/TimeUnit.java:446)在com.leecode.test.MainThread.main(MainThread.java:25))-锁定<0x0000000711ae4be8>(acom.leecode.test.MainThread$1)锁定可拥有的同步器:-无“thread2”#16prio=5os_prio=0cpu=0.00mselapsed=30.16stid=0x00000271b7e8e023nid=waitingforwaiting5监视器条目[0x000000fe383ff000]java.lang.Thread.State:BLOCKED(在对象监视器上)在com.leecode.test.MainThread$1.run(MainThread.java:11)-等待锁定<0x0000000711ae4be8>(一个com.leecode.test.MainThread$1)锁定的可拥有同步器:-无-“thread2”是线程名称。创建线程或线程池时,请务必选择明确的线程名称,以方便排查问题;-优先级=5:线程优先级,无所谓;-tid=0x00000271b7e8e020:线程id,不关心;-nid=0x534c:操作系统映射的线程id,非常关键,后面使用jstack时会加上;-waitingformonitorentry:表示线程在Waitingtoacquirethelock-0x000000fe383ff000:分析线程栈起始地址:主线程已经获取了thread2对象的锁,所以正在执行sleep操作,并且状态为TIMED_WAINTING,而thread2还没有获取thread2对象上的锁,所以处于BLOCKED状态thread2是“waitingtolock<0x0000000711ae4be8>”,即试图获取地址为的对象的锁0x0000000711ae4be8,但是锁被主线程占用了(locked<0x0000000711ae4be8>)。主线程是“waitingoncondition”,表示正在等待某个条件触发。根据jstacktrace,这个线程正在休眠。Linux排错命令:top命令:查看哪个进程占用CPU过多。找到pidtop-Hppid命令:查看问题进程中的线程情况。jstackpid|grepnid-C10:查看对应线程前后10行的状态信息(注意,先用printf'%x\n'nid或者其他方法将十进制nid转为十六进制nid)在线定位(Arthas工具)Arthas是阿里巴巴开源的Java诊断工具。首先下载arthas-boot.jar包,直接通过java-jar命令启动,然后会列出所有正在运行的java进程,让用户选择需要监控的进程,然后进入Arthas运行界面,进入运行界面中的dashboard命令,可以看到被监控进程的所有线程信息(线程ID、名称、状态、CPU使用率、内存使用率、是否是守护线程等)、内存信息(堆内存、Eden区、幸存者区、老年代、方法区)和机器情况。您可以通过sysporp命令查看所有系统属性信息。可以通过sysenv命令查看所有的环境变量信息。JVM命令查看当前进程使用的JVM参数。知道线程id后,可以查看线程thread_id正在执行的线程的堆栈信息。使用sc命令查看有关已加载的类的信息。通过sm类名可以得到该类的所有函数,加上-d可以得到详细的函数信息,也可以指定查看某个函数。您可以通过jad命令反编译代码。这个函数可以帮助我们看到动态代理生成了这样的类(比如通过sc+通配符定位到某个类,然后通过jad命令反编译出这个类的代码)。通过watch命令ognl命令可以查看当前函数的参数/返回值/异常信息,用于动态执行代码,在当前线程环境下执行代码。(ognl'@java.lang.System@out.println("helloognl")')通过使用redefine/path/xxx.class,可以重新加载编译好的类。通常一些热修复可以很容易地实现。其实和IDEA中tomcat的热部署原理是一样的,就是利用ClasssLoader重新加载修改后的类。