Arthas常用命令 | 字数总计: 3.3k | 阅读时长: 14分钟 | 阅读量:
继上一次学习使用 Arthas 之后,今天特此学习了解下 Arthas 在项目中比较好用的几个命令。
启动 Arthas
首先,使用 as.sh
脚本启动 Arthas ,找到需要监控诊断的 Java 进程。
输入前面的数字索引下标,进入 Java 进程,例如 IwAuthApplication 进程需要输入 3 。
接下来就可以使用 Arthas 命令操作了,如果不清楚有哪些命令,在不方便查看官方文档的情况下,或者想要知道当前版本的最新命令,可以直接输入 help
有哪些命令。
知道主命令之后,可以接上参数 -h
了解每个命令的具体使用方法。
命令列表
quit
退出当前 Arthas 客户端,其他 Arthas 客户端不受影响。等同于exit 、logout 、q 三个指令。
使用 quit 命令,只是退出当前 Arthas 客户端,Arthas 的服务器端并没有关闭,所做的修改也不会被重置。
这里所说的修改是指,因为 Arthas 是以 Java agent 方式运行的,它可以修改指定 Java 进程的参数配置等信息,如果 Arthas 服务器端未关闭,配置就不会重置。
stop
关闭 Arthas 服务端,所有 Arthas 客户端全部退出。
关闭 Arthas 服务器之前,会重置掉所有做过的增强类。但是用 redefine 重加载的类内容不会被重置。
jvm
jvm
命令可以查看到当前运行的 Java 进程的 JVM 相关信息。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 $ jvm RUNTIME # JVM 运行环境 ------------------------------------------------------------------------------------------------------------------- MACHINE-NAME 47349@192.168.1.4 JVM-START-TIME 2024-11-20 13:45:29 MANAGEMENT-SPEC-VERSION 3.0 SPEC-NAME Java Virtual Machine Specification SPEC-VENDOR Oracle Corporation SPEC-VERSION 17 VM-NAME OpenJDK 64-Bit Server VM VM-VENDOR Homebrew VM-VERSION 17.0.9+0 INPUT-ARGUMENTS -agentlib:jdwp=transport=dt_socket,address=127.0.0.1:57581,suspend=y,server=n -XX:TieredStopAtLevel=1 -Dspring.output.ansi.enabled=always -Dcom.sun.management.jmxremote -Dspring.jmx.enabled=true -Dspring.liveBeansView.mbeanDomain -Dspring.application.admin.enabled=true -javaagent:/Users/wangfarui/Library/Caches/JetBrains/IntelliJIdea2022.2/captureA gent/debugger-agent.jar=file:/private/var/folders/wv/0ljm0h256y1_f_fgv2yw62rh000 0gn/T/capture.props # 通过这里可以发现,Idea在通过Debug方式启动Java程序时,会嵌入一个javaagent程序 -Dfile.encoding=UTF-8 CLASS-PATH [] BOOT-CLASS-PATH LIBRARY-PATH /Users/wangfarui/Library/Java/Extensions:/Library/Java/Extensions:/Network/Libra ry/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:. ------------------------------------------------------------------------------------------------------------------- CLASS-LOADING # ClassLoader ------------------------------------------------------------------------------------------------------------------- LOADED-CLASS-COUNT 21095 # 当前类加载数量 TOTAL-LOADED-CLASS-COUNT 21095 # 总共的类加载数量 UNLOADED-CLASS-COUNT 0 # 已卸载的类数量 IS-VERBOSE false ------------------------------------------------------------------------------------------------------------------- COMPILATION ------------------------------------------------------------------------------------------------------------------- NAME HotSpot 64-Bit Tiered Compilers TOTAL-COMPILE-TIME 2875 [time (ms)] ------------------------------------------------------------------------------------------------------------------- GARBAGE-COLLECTORS # 垃圾收集器 ------------------------------------------------------------------------------------------------------------------- G1 Young Generation name : G1 Young Generation [count/time (ms)] collectionCount : 20 # G1新生代收集次数 collectionTime : 79 # G1新生代收集时间 G1 Old Generation name : G1 Old Generation [count/time (ms)] collectionCount : 0 # G1老年代收集次数 collectionTime : 0 # G1老年代收集时间 ------------------------------------------------------------------------------------------------------------------- MEMORY-MANAGERS # 内存管理器 ------------------------------------------------------------------------------------------------------------------- CodeCacheManager CodeCache Metaspace Manager Metaspace Compressed Class Space G1 Young Generation G1 Eden Space G1 Survivor Space G1 Old Gen G1 Old Generation G1 Eden Space G1 Survivor Space G1 Old Gen ------------------------------------------------------------------------------------------------------------------- MEMORY # 内存信息 ------------------------------------------------------------------------------------------------------------------- HEAP-MEMORY-USAGE init : 268435456(256.0 MiB) [memory in bytes] used : 115123728(109.8 MiB) # 堆已使用的内存 committed : 169869312(162.0 MiB) max : 4294967296(4.0 GiB) # 堆最大内存 即 -Xmx NO-HEAP-MEMORY-USAGE init : 2555904(2.4 MiB) [memory in bytes] used : 136267744(130.0 MiB) # 元空间已使用的内存 committed : 137297920(130.9 MiB) max : -1(-1 B) # -1表示元空间无限大 PENDING-FINALIZE-COUNT 0 ------------------------------------------------------------------------------------------------------------------- OPERATING-SYSTEM # 操作系统信息 ------------------------------------------------------------------------------------------------------------------- OS Mac OS X ARCH aarch64 PROCESSORS-COUNT 8 LOAD-AVERAGE 6.015625 VERSION 14.6.1 ------------------------------------------------------------------------------------------------------------------- THREAD ------------------------------------------------------------------------------------------------------------------- COUNT 114 # JVM 当前活跃的线程数 DAEMON-COUNT 61 # JVM 当前活跃的守护线程数 PEAK-COUNT 134 # 从 JVM 启动开始曾经活着的最大线程数 STARTED-COUNT 676 # 从 JVM 启动开始总共启动过的线程次数 DEADLOCK-COUNT 0 # JVM 当前死锁的线程数 ------------------------------------------------------------------------------------------------------------------- FILE-DESCRIPTOR ------------------------------------------------------------------------------------------------------------------- MAX-FILE-DESCRIPTOR-COUNT -1 # JVM 进程最大可以打开的文件描述符数 OPEN-FILE-DESCRIPTOR-COUNT -1 # JVM 当前打开的文件描述符数
thread
作用:查看当前线程信息,查看线程的堆栈。
参数名称
参数说明
id
线程 id
[n:]
指定最忙的前 N 个线程并打印堆栈
[b]
找出当前阻塞其他线程的线程
[i <value>
]
指定 cpu 使用率统计的采样间隔,单位为毫秒,默认值为 200
[--all]
显示所有匹配的线程
直接执行 thread
命令,表示按照 CPU 增量时间降序排列,打印第一页所有线程数据。
thread --state
查看指定状态的线程。例如 thread --state WAITING
查询所有等待线程。
memory
作用:查看 JVM 内存信息。
直接执行 memory
命令,结果信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ memory Memory used total max usage heap 97M 162M 4096M 2.37% g1_survivor_space 8M 10M -1 80.16% g1_eden_space 34M 66M -1 51.52% g1_old_gen 55M 86M 4096M 1.34% nonheap 132M 133M -1 99.27% metaspace 98M 98M -1 99.36% compressed_class_space 13M 13M 1024M 1.31% codecache 20M 20M 48M 43.06% mapped 0K 0K - 0.00% direct 86M 86M - 100.00% mapped - 'non-volatile memory' 0K 0K - 0.00%
可以看到主要分为 heap
和 nonheap
两块信息。
stack
作用:输出当前方法被调用的调用路径。
很多时候我们都知道一个方法被执行,但这个方法被执行的路径非常多,或者你根本就不知道这个方法是从那里被执行了,此时你需要的是 stack 命令。
stack
是一个监控命令,只能监控 stack 命令执行之后的 JVM 运行状态。它主要是监控具体某个类的某个方法,输出该方法的调用链路。
参数名称
参数说明
class-pattern
类名表达式匹配
method-pattern
方法名表达式匹配
condition-express
条件表达式
[E]
开启正则表达式匹配,默认为通配符匹配
[n:]
执行次数限制
[m <arg>]
指定 Class 最大匹配数量,默认值为 50。长格式为[maxMatch <arg>]
。
[E] 是一个观察表达式,主要由 ognl 表达式组成,具体表达式用法可以参考:
例如,执行 stack com.itwray.iw.auth.service.impl.AuthUserServiceImpl loginByPassword
,等待有线程触发执行到 AuthUserServiceImpl#loginByPassword
方法时,Arthas 就会打印调用链路。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ stack com.itwray.iw.auth.service.impl.AuthUserServiceImpl loginByPassword Press Q or Ctrl+C to abort. Affect(class count: 2 , method count: 2) cost in 187 ms, listenerId: 1 ts=2024-11-20 14:36:19.412;thread_name=http-nio-18001-exec-5;id=122;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@2f29e630 @com.itwray.iw.auth.service.impl.AuthUserServiceImpl.loginByPassword() at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2) at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:351) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:196) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765) at org.springframework.aop.aspectj.MethodInvocationProceedingJoinPoint.proceed(MethodInvocationProceedingJoinPoint.java:89) at com.itwray.iw.starter.redis.lock.RedisDistributedLockAspect.around(RedisDistributedLockAspect.java:63) at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(NativeMethodAccessorImpl.java:-2) at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77) at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:568) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethodWithGivenArgs(AbstractAspectJAdvice.java:637) at org.springframework.aop.aspectj.AbstractAspectJAdvice.invokeAdviceMethod(AbstractAspectJAdvice.java:627) at org.springframework.aop.aspectj.AspectJAroundAdvice.invoke(AspectJAroundAdvice.java:71) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:173) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:184) at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:765) at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:717) at com.itwray.iw.auth.service.impl.AuthUserServiceImpl$$SpringCGLIB$$0.loginByPassword(<generated>:-1) at com.itwray.iw.auth.controller.AuthLoginController.loginByPassword(AuthLoginController.java:60) ...
需要注意的是,在上面这段命令执行示例中,有一段输出内容如下:
1 Affect(class count: 2 , method count: 2) cost in 187 ms, listenerId: 1
它表示监听到两个符合条件的类,两个符合条件的方法。然而在项目中我是只写了一个AuthUserServiceImpl#loginByPassword方法的,这是因为 Spring 的@Service 对其进行了增强,生成了一个 AuthUserServiceImpl$$SpringCGLIB$$0.loginByPassword 代理类。
watch
watch
命令与stack
命令一样,也是监控方法运行的,不过它的主要作用是观察方法的调用情况,例如返回值
、抛出异常
、入参
。
参数名称
参数说明
class-pattern
类名表达式匹配
method-pattern
函数名表达式匹配
express
观察表达式,默认值:{params, target, returnObj}
condition-express
条件表达式
[b]
在函数调用之前 观察
[e]
在函数异常之后 观察
[s]
在函数返回之后 观察
[f]
在函数结束之后 (正常返回和异常返回)观察
[E]
开启正则表达式匹配,默认为通配符匹配
[x:]
指定输出结果的属性遍历深度,默认为 1,最大值是 4
[m <arg>]
指定 Class 最大匹配数量,默认值为 50。长格式为[maxMatch <arg>]
。
特别说明 :
watch 命令定义了 4 个观察事件点,即 -b
函数调用前,-e
函数异常后,-s
函数返回后,-f
函数结束后
4 个观察事件点 -b
、-e
、-s
默认关闭,-f
默认打开,当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出
这里要注意函数入参
和函数出参
的区别,有可能在中间被修改导致前后不一致,除了 -b
事件点 params
代表函数入参外,其余事件都代表函数出参
当使用 -b
时,由于观察事件点是在函数调用前,此时返回值或异常均不存在
在 watch 命令的结果里,会打印出location
信息。location
有三种可能值:AtEnter
,AtExit
,AtExceptionExit
。对应函数入口,函数正常 return,函数抛出异常。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ watch com.itwray.iw.auth.service.impl.AuthUserServiceImpl loginByPassword Press Q or Ctrl+C to abort. Affect(class count: 2 , method count: 2) cost in 117 ms, listenerId: 2 method=com.itwray.iw.auth.service.impl.AuthUserServiceImpl.loginByPassword location=AtExit ts=2024-11-20 14:47:20.846; [cost=116.9355ms] result=@ArrayList[ @Object[][isEmpty=false;size=1], @AuthUserServiceImpl[com.itwray.iw.auth.service.impl.AuthUserServiceImpl@7aae5a4c], @UserInfoVo[UserInfoVo(name=wray, tokenName=iwtoken, tokenValue=0a85a7e9-fed8-4be2-b50a-d6c981b81f98, avatar=https://1.com/img/border-collie-8501579_1920.jpg)], ] method=com.itwray.iw.auth.service.impl.AuthUserServiceImpl$$SpringCGLIB$$0.loginByPassword location=AtExit ts=2024-11-20 14:47:20.851; [cost=142.679166ms] result=@ArrayList[ @Object[][isEmpty=false;size=1], @AuthUserServiceImpl$$SpringCGLIB$$0[com.itwray.iw.auth.service.impl.AuthUserServiceImpl@7aae5a4c], @UserInfoVo[UserInfoVo(name=wray, tokenName=iwtoken, tokenValue=0a85a7e9-fed8-4be2-b50a-d6c981b81f98, avatar=https://1.com/img/border-collie-8501579_1920.jpg)], ]
注意:同stack
命令一样,watch
命令也会监听代理方法。
trace
trace
和stack
、watch
命令一样,也是监听方法的,它的主要作用是监听方法的调用链路上每个节点的耗时。
参数名称
参数说明
class-pattern
类名表达式匹配
method-pattern
方法名表达式匹配
condition-express
条件表达式
[E]
开启正则表达式匹配,默认为通配符匹配
[n:]
命令执行次数,默认值为 100。
#cost
方法执行耗时
[m <arg>]
指定 Class 最大匹配数量,默认值为 50。长格式为[maxMatch <arg>]
。
示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 $ trace com.itwray.iw.auth.service.impl.AuthUserServiceImpl loginByPassword Press Q or Ctrl+C to abort. Affect(class count: 2 , method count: 2) cost in 155 ms, listenerId: 3 `---ts=2024-11-20 14:59:55.762;thread_name=http-nio-18001-exec-2;id=119;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@2f29e630 `---[120.320417ms] com.itwray.iw.auth.service.impl.AuthUserServiceImpl$$SpringCGLIB$$0:loginByPassword() `---[99.89% 120.190292ms ] org.springframework.cglib.proxy.MethodInterceptor:intercept() `---[92.07% 110.65475ms ] com.itwray.iw.auth.service.impl.AuthUserServiceImpl:loginByPassword() +---[0.06% 0.062209ms ] com.itwray.iw.auth.model.dto.LoginPasswordDto:getUsername() #59 +---[4.60% 5.094084ms ] com.itwray.iw.auth.dao.AuthUserDao:queryOneByUsername() #59 +---[0.01% 0.011792ms ] com.itwray.iw.auth.model.dto.LoginPasswordDto:getPassword() #69 +---[0.01% 0.013333ms ] com.itwray.iw.auth.model.entity.AuthUserEntity:getPassword() #69 +---[94.27% 104.315167ms ] cn.hutool.crypto.digest.BCrypt:checkpw() #69 +---[0.01% 0.005542ms ] com.itwray.iw.auth.model.entity.AuthUserEntity:getId() #80 +---[0.61% 0.676208ms ] com.itwray.iw.starter.redis.RedisUtil:set() #80 +---[0.05% 0.056ms ] com.itwray.iw.auth.service.impl.AuthUserServiceImpl:setTokenValue() #83 +---[0.00% 0.003834ms ] com.itwray.iw.auth.model.vo.UserInfoVo:<init>() #86 +---[0.00% 0.003541ms ] com.itwray.iw.auth.model.entity.AuthUserEntity:getName() #87 +---[0.00% 0.003333ms ] com.itwray.iw.auth.model.vo.UserInfoVo:setName() #87 +---[0.00% 0.003042ms ] com.itwray.iw.auth.model.entity.AuthUserEntity:getAvatar() #88 +---[0.00% 0.002833ms ] com.itwray.iw.auth.model.vo.UserInfoVo:setAvatar() #88 +---[0.00% 0.003375ms ] com.itwray.iw.auth.model.vo.UserInfoVo:setTokenName() #89 `---[0.00% 0.002834ms ] com.itwray.iw.auth.model.vo.UserInfoVo:setTokenValue() #90
classloader
作用:将 JVM 中所有的 classloader 的信息统计出来,并可以展示继承树,urls 等。
参数名称
参数说明
[l]
按类加载实例进行统计
[t]
打印所有 ClassLoader 的继承树
[a]
列出所有 ClassLoader 加载的类,请谨慎使用
[c:]
ClassLoader 的 hashcode
[classLoaderClass:]
指定执行表达式的 ClassLoader 的 class name
[c: r:]
用 ClassLoader 去查找 resource
[c: load:]
用 ClassLoader 去加载指定的类
直接执行classloader
,可以看到所有 classloader 实例数量以及它们各自加载的类数量。
1 2 3 4 5 6 7 8 9 $ classloader name numberOfInstances loadedCountTotal jdk.internal.loader.ClassLoaders$AppClassLoader 1 15968 BootstrapClassLoader 1 5228 com.taobao.arthas.agent.ArthasClassloader 1 2063 jdk.internal.loader.ClassLoaders$PlatformClassLoader 1 154 jdk.internal.reflect.DelegatingClassLoader 112 112 sun.reflect.misc.MethodUtil 1 1 Affect(row-cnt:6) cost in 49 ms.
heapdump
作用:生成 dump 文件。类似于 jmap 命令的 heap dump 功能。
dump 到指定文件:heapdump arthas-output/dump.hprof # 注意,这里使用相对路径时,会使用监听的Java进程的运行路径作为根路径。
只 dump live 对象:heapdump --live /tmp/dump.hprof
如果有项目运行环境,可以直接在运行环境执行 jps 找到对应的 pid ,再使用 jcmd pid GC.heap_dump /tmp/dump.hprof
的方式生成 dump 文件。注意 jcmd
是JDK工具,要确保运行环境有安装JDK。
Arthas 实战场景
通过arthas怎样排查项目中,哪个对象泄露了,或者占用内存太大
参考地址:https://arthas.aliyun.com/doc/expert/user-question-history13509.html