【JVM】Arthas相关分析命令
【JVM】Arthas相关分析命令【一】下载和启动【二】JVM相关的核心功能1类别 1JVM基础信息监控2类别 2类与类加载器信息3类别 3性能分析与火焰图【三】JVM相关的命令【1】JVM相关命令1dashboard实时数据面板2Thread线程相关堆栈信息3jvm4sysprop查看/修改属性5sysenv查看JVM环境属性6vmpotion查看JVM中选项7getstatic获取静态成员变量8ognl执行ognl表达式【2】类和类加载器(class/classLoader)1sc查看类信息2sm查看已加载方法信息3编译与反编译jad、mc、redefine4dump保存已加载字节码文件到本地5classloader获取类加载器的信息【四】JVM分析案例1准备测试的案例代码死锁、内存泄漏多种情况、死循环2运行代码启动arthas3排查定位CPU飙高问题死循环4排查定位死锁问题死锁【一】下载和启动下载地址https://arthas.aliyun.com/doc/download.html直接下载全量包Arthas 支持在 Linux/Unix/Mac 等平台上一键安装请复制以下内容并粘贴到命令行中敲 回车 执行即可curl-L https://arthas.aliyun.com/install.sh|sh1用 as.sh 启动解压后在文件夹里有as.sh直接用./as.sh的方式启动./as.sh打印帮助信息./as.sh-h2用 arthas-boot 启动或者在解压后在文件夹里有arthas-boot.jar直接用java -jar的方式启动java-jar arthas-boot.jar打印帮助信息java-jar arthas-boot.jar-h3注意在Linux上启动arthas的时候要注意是不是root权限并且注意项目启动的用户和当前启动arthas的用户是不是相同【二】JVM相关的核心功能1类别 1JVM基础信息监控1dashboard: ​核心命令。提供一个实时更新的仪表盘一站式查看1-线程信息显示CPU占用高的线程2-内存信息堆内存、元空间等使用情况3-垃圾回收GC次数和时间4-主机信息CPU、负载5-运行时信息6-​作用​快速对应用的健康状况有一个整体了解快速发现资源瓶颈。2jvm: 查看详细的JVM信息包括线程、内存、GC、加载类、操作系统等信息。作用​深入了解JVM的详细配置和状态。3thread: 查看所有线程的信息以及单个线程的堆栈。1-thread : 查看指定线程的堆栈非常适合定位CPU飙升问题。2-thread -b: 直接找出阻塞其他线程的根因线程​查找死锁。3-作用​诊断线程相关问题如CPU占用高、死锁、响应慢。4sysenv/ sysprop: 查看系统的环境变量和JVM的系统属性。作用​确认应用运行时读取的配置是否正确例如Spring的active profile、数据库连接地址等。2类别 2类与类加载器信息1sc(Search Class)​: 查看已加载的类信息包括来源哪个Jar包。1-sc -d com.example.MyClass: 显示类的详细信息包括其类加载器。2-​作用​排查类冲突如NoSuchMethodError, NoClassDefFoundError确认加载的类版本是否正确。2​sm(Search Method)​: 查看类的方法信息。1-sm com.example.MyClass *Method: 列出类中所有的方法。2-​作用​确认方法是否存在以及它的描述符。3​classloader: 查看类加载器的继承树、加载了哪些类、是否存在重复的Jar包或类。1-​作用​深入诊断类加载冲突、内存泄漏类加载器无法被GC会导致其加载的所有类都无法卸载。3类别 3性能分析与火焰图1​profiler: 生成火焰图​Flame Graph一种非常直观的性能分析图表。1-profiler start: 开始采样。2-profiler stop: 停止采样并生成火焰图文件。3-​作用​从宏观上定位CPU占用热点直观地看到代码的“火”最耗CPU在哪里。【三】JVM相关的命令【1】JVM相关命令1dashboard实时数据面板查看当前系统的实时数据面板。dashboard输入 q 或者 CtrlC 可以退出dashboard命令数据说明IDJava级别的线程ID注意这个ID不能跟jstack中的nativeID一一对应 NAME线程名GROUP线程组名 PRIORITY线程优先级,1~10之间的数字越大表示优先级越高 STATE线程的状态 CPU%线程消耗的cpu占比采样100ms将所有线程在这100ms内的cpu使用量求和再算出每个线程的cpu使用占比。 TIME线程运行总时间数据格式为分秒 INTERRUPTED线程当前的中断位状态 DAEMON是否是daemon线程2Thread线程相关堆栈信息线程相关堆栈信息。1Arthas支持管道可以用 thread 1 | grep ‘main(’ 查找到main class。thread 1|grepmain(thread# 显示所有线程的信息thread 1# 显示1号线程的运行堆栈thread-b# 查看阻塞的线程信息thread-n 3# 查看最忙的3个线程并打印堆栈thread-i 1000-n 3# 指定采样时间间隔每过1000毫秒采样显示最占时间的3个线程查看处于等待状态的线程WAITING、BLOCKED thread--state WAITING2死锁线程查看thread# 查看线程状态thread-b# 查看阻塞的线程信息3jvmTHREAD相关COUNTJVM当前活跃的线程数 DAEMON-COUNT JVM当前活跃的守护线程数 PEAK-COUNT从JVM启动开始曾经活着的最大线程数 STARTED-COUNT从JVM启动开始总共启动过的线程次数 DEADLOCK-COUNTJVM当前死锁的线程数文件描述符相关MAX-FILE-DESCRIPTOR-COUNTJVM进程最大可以打开的文件描述符数OPEN-FILE-DESCRIPTOR-COUNTJVM当前打开的文件描述符数4sysprop查看/修改属性sysprop# 查看所有属性sysprop java.version# 查看单个属性支持通过tab补全修改某个属性sysprop user.country user.countryUS5sysenv查看JVM环境属性查看当前JVM的环境属性(System Environment Variables)# 查看所有环境变量sysenv# 查看单个环境变量sysenv USER6vmpotion查看JVM中选项查看JVM中选项可以修改# 查看所有的选项vmoption# 查看指定的选项vmoption PrintGCDetails# 更新指定的选项vmoption PrintGCDetails true更新某一个值7getstatic获取静态成员变量获取静态成员变量# 语法getstatic 类名 属性名# 显示demo.MathGame类中静态属性randomgetstatic demo.MathGame random8ognl执行ognl表达式执行ognl表达式这是从3.0.5版本新增的功能。1调用静态函数# 获取系统变量中值并且打印(只会打印有返回值函数)ognljava.lang.Systemout.println(hello)2获取静态类的静态字段# 获取代码中的运行返回值ognldemo.MathGamerandom3执行多行表达式赋值给临时变量返回一个List# 计算value1、value2值并存在List集合中ognl#value1SystemgetProperty(java.home), #value2SystemgetProperty(java.runtime.name), {#value1, #value2}【2】类和类加载器(class/classLoader)1sc查看类信息查看类的信息(sc: Search Class)查看JVM已加载的类信息“Search-Class” 的简写这个命令能搜索出所有已经加载到 JVM 中的 Class 信息sc 默认开启了子类匹配功能也就是说所有当前类的子类也会被搜索出来想要精确的匹配请打开options disable-sub-class true开关。# 模糊搜索demo包下所有的类scdemo.*# 打印类的详细信息sc-d demo.MathGame2sm查看已加载方法信息查看已加载方法信息(“Search-Method” )查看已加载类的方法信息“Search-Method” 的简写这个命令能搜索出所有已经加载了 Class 信息的方法信息。sm 命令只能看到由当前类所声明 (declaring) 的方法父类则无法看到。# 显示String类加载的方法sm java.lang.String# 查看方法信息sm demo.MathGame# 查看方法信息(详细信息-d)sm-d demo.MathGame3编译与反编译jad、mc、redefine1jad反编译已加载类源码反编译指定已加载类源码jad 命令将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码便于你理解业务逻辑在 Arthas Console 上反编译出来的源码是带语法高亮的阅读更方便。当然反编译出来的 java 代码可能会存在语法错误但不影响你进行阅读理解。# 反编译MathGame方法jad demo.MathGame# 反编绎时只显示源代码(排除ClassLoader信息)。# 默认情况下反编译结果里会带有ClassLoader信息通过--source-only选项可以只打印源代码。方便和mc/redefine命令结合使用。jad--source-only demo.MathGame# 反编译到指定文件中jad--source-only demo.MathGame Hello.java# 只反编译mathGame类型中main方法jad demo.MathGame main2mc编译Java代码内存编译Memory Compiler/内存编译器编译.java文件生成.class# 在内存中编译Hello.java为Hello.classmc/root/Hello.java# 可以通过-d命令指定输出目录mc-d/root/bbb/root/Hello.java3redefine加载外部.class文件加载外部的.class文件redefine到JVM里。注意 redefine后的原来的类不能恢复redefine有可能失败比如增加了新的field。reset命令对redefine的类无效。如果想重置需要redefine原始的字节码。redefine命令和jad/watch/trace/monitor/tt等命令会冲突。执行完redefine之后如果再执行上面提到的命令则会把redefine的字节码重置。redefine的限制1不允许新增加field/method2正在跑的函数没有退出不能生效比如下面新增加的System.out.println只有run()函数里的会生效。# 1. 使用jad反编译demo.MathGame输出到/root/MathGame.javajad--source-only demo.MathGame /root/MathGame.java# 2.按上面的代码编辑完毕以后使用mc内存中对新的代码编译mc/root/MathGame.java-d/root# 3.使用redefine命令加载新的字节码redefine/root/demo/MathGame.class4dump保存已加载字节码文件到本地将已加载类的字节码文件保存在特定目录logs/arthas/classdump。不同的类加载器放在不同的目录下。dump作用将正在JVM中运行的程序的字节码文件提取出来保存在logs相应的目录下# 把String类的字节码文件保存到~/logs/arthas/classdump/目录下dump java.lang.String# 把demo包下所有的类的字节码文件保存到~/logs/arthas/classdump/目录下dump demo.*5classloader获取类加载器的信息获取类加载器的信息作用1classloader 命令将 JVM 中所有的classloader的信息统计出来并可以展示继承树urls等。2可以让指定的classloader去getResources打印出所有查找到的resources的url。对于ResourceNotFoundException异常比较有用。# 默认按类加载器的类型查看统计信息classloader# 按类加载器的实例查看统计信息可以看到类加载的hashCodeclassloader-l# 查看ClassLoader的继承树classloader-t# 通过类加载器的hash查看此类加载器实际所在的位置classloader-c 680f2737# 使用ClassLoader去查找指定资源resource所在的位置classloader-c 680f2737-r META-INF/MANIFEST.MF# 使用ClassLoader去查找类的class文件所在的位置classloader-c 680f2737-r java/lang/String.class# 使用ClassLoader去加载类classloader-c 70dea4e--load java.lang.Stringclassloader命令主要作用有哪些1显示所有类加载器的信息2获取某个类加载器所在的jar包3获取某个资源在哪个jar包中4加载某个类【四】JVM分析案例1准备测试的案例代码死锁、内存泄漏多种情况、死循环publicclassjvmUpdate02{privatestaticHashSethashSetnewHashSet();publicstaticvoidmain(String[]args){//模拟死锁deadThread();//创建对象addHashSetThread();//模拟高CPUcpuHigh();}/** 模拟死锁 * MethodName: deadThread * Author: AllenSun * Date: 2022/11/9 上午12:59 */privatestaticvoiddeadThread(){Objectlock1newObject();Objectlock2newObject();newThread(()-{synchronized(lock1){try{System.out.println(线程1启动);Thread.sleep(5000);}catch(InterruptedExceptione){e.printStackTrace();}synchronized(lock2){System.out.println(线程1结束);}}}).start();newThread(()-{synchronized(lock2){try{System.out.println(线程2启动);Thread.sleep(5000);}catch(InterruptedExceptione){e.printStackTrace();}synchronized(lock1){System.out.println(线程2结束);}}}).start();System.out.println(主线程结束);}/**不断的向HashSet集合添加数据 * MethodName: addHashSetThread * Author: AllenSun * Date: 2022/11/9 上午1:03 */publicstaticvoidaddHashSetThread(){newThread(()-{intcount0;while(true){try{hashSet.add(countcount);Thread.sleep(10000);count;}catch(InterruptedExceptione){e.printStackTrace();}}}).start();}/** 模拟CPU飙升 * MethodName: cpuHigh * Author: AllenSun * Date: 2022/11/9 上午1:05 */publicstaticvoidcpuHigh(){newThread(()-{while(true){}}).start();}}2运行代码启动arthas先运行代码然后启动arthasjava -jar arthas-boot.jar3排查定位CPU飙高问题死循环1输入dashboard仪表盘内存使用情况综合界面可以看出来下面线程id为14的线程占用的CPU较高接下来对其进行分析2thread 线程id查看问题线程的具体情况ctrlc退出仪表盘thread 14查看对应线程的情况找到92行果然看到方法体中有一个无限循环导致了CPU飙高3thread -n 1列出CPU占用前1的线程4排查定位死锁问题死锁1thread -b定位死锁问题出现的位置定位到42行这个lock2在下面被尝试获取失败导致产生的死锁2thread获取所有线程的情况根据状态State的字段【BLOCKED】可以找到具体产生死锁的方法

相关新闻