1. 简介
Arthas 是一款线上监控诊断产品,通过全局视角实时查看应用 load、内存、gc、线程的状态信息,并能在不修改应用代码的情况下,对业务问题进行诊断,包括查看方法调用的出入参、异常,监测方法执行耗时,类加载信息等,大大提升线上问题排查效率。
Arthas(阿尔萨斯)能为你做什么?
Arthas 是 Alibaba 开源的 Java 诊断工具,当你遇到以下类似问题而束手无策时,Arthas可以帮助你解决:
- 这个类从哪个 jar 包加载的?为什么会报各种类相关的 Exception?
- 我改的代码为什么没有执行到?难道是我没 commit?分支搞错了?
- 遇到问题无法在线上 debug,难道只能通过加日志再重新发布吗?
- 线上遇到某个用户的数据处理有问题,但线上同样无法 debug,线下无法重现!
- 是否有一个全局视角来查看系统的运行状况?
- 有什么办法可以监控到 JVM 的实时运行状态?
- 怎么快速定位应用的热点,生成火焰图?
- 怎样直接从 JVM 内查找某个类的实例?
Arthas
支持 JDK 6+,支持 Linux/Mac/Windows,采用命令行交互模式,同时提供丰富的 Tab
自动补全功能,进一步方便进行问题的定位和诊断。
2.安装和使用
-- 下载arthas工具包
curl -O https://arthas.aliyun.com/arthas-boot.jar
-- 启动arthas工具包
java -jar arthas-boot.jar
公司内网需要下载arthas全量包,然后手工上传至服务器,下载地址:https://arthas.aliyun.com/download/latest_version?mirror=aliyun
3.常用命令
3.1 基础命令
3.3.1 help (重要)
功能:显示所有arthas命令,每个命令都可用使用-h参数,查看命令帮助信息
[arthas@31278]$ dashboard -h
USAGE:
dashboard [-h] [-i <value>] [-n <value>]
SUMMARY:
Overview of target jvm's thread, memory, gc, vm, tomcat info.
EXAMPLES:
dashboard
dashboard -n 10
dashboard -i 2000
WIKI:
https://arthas.aliyun.com/doc/dashboard
OPTIONS:
-h, --help this help
-i, --interval <value> The interval (in ms) between two executions, default is 5000 ms.
-n, --number-of-execution <value> The number of times this command will be executed.
3.3.2 cat
功能:显示文件内容,和 linux 里的 cat 命令类似
3.3.3 grep
功能:匹配查找,和 linux 里的 grep 命令类似
3.3.4 pwd
功能:返回当前的工作目录,和 linux 命令类似
[arthas@31278]$ pwd
/Users/**/Downloads/jacoco-0.8.9/lib
3.3.5 cls(重要)
功能: 清空当前屏幕区域
3.3.6 session
功能:查看当前会话的信息
[arthas@31278]$ session
Name Value
--------------------------------------------------
JAVA_PID 31278
SESSION_ID d5d6476d-21da-4f2f-acef-c018fdd5bfb6
3.3.7 base64
功能:base64 编码转换,和 linux 里的 base64 命令类似
- 对文件进行base64编码
[arthas@31278]$ echo 'abcd' > /tmp/test.txt
[arthas@31278]$ cat /tmp/test.txt
abcd
[arthas@31278]$ base64 /tmp/test.txt
YWJjZAo=
- 对文件进行 base64 编码并把结果保存到文件里
[arthas@31278]$ base64 --input /tmp/test.txt --output /tmp/result.txt
[arthas@31278]$ cat /tmp/result.txt
YWJjZAo=
- 用 base64 解码文件
[arthas@31278]$ base64 -d /tmp/result.txt
abcd
- 用 base64 解码文件并保存结果到文件里
[arthas@31278]$ base64 -d /tmp/result.txt --output /tmp/resut2.txt
[arthas@31278]$ cat /tmp/resut2.txt
abcd
3.3.8 version
功能:输出当前目标 Java 进程所加载的 Arthas 版本号
[arthas@31278]$ version
3.6.6
3.3.9 echo
功能:打印参数,和 linux 里的 echo 命令类似
3.3.10 history(重要)
功能:打印命令历史
[arthas@31278]$ history 5
35 version
36 echo "hello"
37 history
38 history -h
39 history 5
3.3.11 keymap
功能:Arthas 快捷键列表及自定义快捷键
[arthas@31278]$ keymap
Shortcut Description Name
"\C-a" Ctrl + a beginning-of-line
"\C-e" Ctrl + e end-of-line
"\C-f" Ctrl + f forward-word
"\C-b" Ctrl + b backward-word
"\e[D" Left arrow backward-char
"\e[C" Right arrow forward-char
"\e[A" Up arrow history-search-backward
"\e[B" Down arrow history-search-forward
3.3.12 quit(重要)
功能:退出当前 Arthas 客户端,其他 Arthas 客户端不受影响
[arthas@31278]$ quit
**deMacBook-Pro:arthas_demo **$
3.3.13 stop(重要)
功能:关闭 Arthas 服务端,所有 Arthas 客户端全部退出
[arthas@31278]$ stop
Resetting all enhanced classes ...
Affect(class count: 0 , method count: 0) cost in 6 ms, listenerId: 0
Arthas Server is going to shutdown...
[arthas@31278]$ session (acbf60b7-9cf6-4f50-b030-b861c0f64ca6) is closed because server is going to shutdown.
3.3.14 reset
功能:重置增强类,将被 Arthas 增强过的类全部还原,Arthas 服务端关闭时会重置所有增强过的类
[arthas@31278]$ reset
Affect(class count: 0 , method count: 0) cost in 0 ms, listenerId: 0
3.2 jvm相关命令
3.2.1 dashboard(重要)
功能:当前系统的实时数据面板,可以显示线程、内存、堆栈、GC、Runtime等信息,刷新实时数据的时间间隔 (ms),默认 5000ms
[arthas@31278]$ dashboard
ID NAME GROUP PRIORITY STATE %CPU DELTA_TI TIME INTERRUP DAEMON
-1 C2 CompilerThread0 - -1 - 0.0 0.000 0:7.844 false true
-1 C2 CompilerThread1 - -1 - 0.0 0.000 0:7.694 false true
35 DestroyJavaVM main 5 RUNNABLE 0.0 0.000 0:6.029 false false
-1 VM Periodic Task Thread - -1 - 0.0 0.000 0:5.781 false true
-1 C1 CompilerThread2 - -1 - 0.0 0.000 0:3.371 false true
18 Catalina-utility-2 main 1 TIMED_WA 0.0 0.000 0:0.752 false false
17 Catalina-utility-1 main 1 WAITING 0.0 0.000 0:0.740 false false
-1 VM Thread - -1 - 0.0 0.000 0:0.657 false true
33 http-nio-9091-Poller main 5 RUNNABLE 0.0 0.000 0:0.443 false true
20 mysql-cj-abandoned-connect main 5 TIMED_WA 0.0 0.000 0:0.294 false true
-1 GC task thread#2 (Parallel - -1 - 0.0 0.000 0:0.264 false true
-1 GC task thread#0 (Parallel - -1 - 0.0 0.000 0:0.239 false true
-1 GC task thread#1 (Parallel - -1 - 0.0 0.000 0:0.230 false true
83 arthas-NettyHttpTelnetBoot system 5 RUNNABLE 0.0 0.000 0:0.228 false true
-1 GC task thread#3 (Parallel - -1 - 0.0 0.000 0:0.227 false true
90 arthas-NettyHttpTelnetBoot system 5 RUNNABLE 0.0 0.000 0:0.108 false true
93 arthas-NettyHttpTelnetBoot system 5 RUNNABLE 0.0 0.000 0:0.094 false true
91 arthas-command-execute system 5 TIMED_WA 0.0 0.000 0:0.051 false true
Memory used total max usage GC
heap 79M 341M 1820M 4.34% gc.ps_scavenge.count 12
ps_eden_space 50M 234M 657M 7.65% gc.ps_scavenge.time(ms) 196
ps_survivor_space 0K 10240K 10240K 0.00% gc.ps_marksweep.count 3
ps_old_gen 28M 97M 1365M 2.11% gc.ps_marksweep.time(ms) 490
nonheap 81M 85M -1 95.49%
code_cache 15M 15M 240M 6.30%
metaspace 58M 61M -1 95.05%
compressed_class_space 7M 8M 1024M 0.74%
direct 0K 0K - 0.00%
mapped 0K 0K - 0.00%
Runtime
os.name Mac OS X
os.version 10.16
java.version 1.8.0_181
java.home /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Co
ntents/Home/jre
systemload.average 4.72
processors 4
3.2.2 thread(重要)
功能:查看当前线程信息,显示线程堆栈
- thread -n 2 -i 5000:展示当前进程中5s内最忙的前2个线程并打印堆栈信息
[arthas@31278]$ thread -n 2 -i 5000
"arthas-command-execute" Id=91 cpuUsage=0.87% deltaTime=1ms time=128ms RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:448)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:206)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.access$100(AnnotatedCommandImpl.java:18)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:111)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl$ProcessHandler.handle(AnnotatedCommandImpl.java:108)
at com.taobao.arthas.core.shell.system.impl.ProcessImpl$CommandProcessTask.run(ProcessImpl.java:385)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:180)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:293)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
"C1 CompilerThread2" [Internal] cpuUsage=0.68% deltaTime=1ms time=3662ms
- 展示所有线程信息,默认按照cpu增量时间降序排列,只显示第一页数据 (thread --all显示所有匹配线程)
[arthas@31278]$ thread
Threads Total: 48, NEW: 0, RUNNABLE: 11, BLOCKED: 0, WAITING: 21, TIMED_WAITING: 6, TERMINATED: 0, Internal t
hreads: 10
ID NAME GROUP PRIORITY STATE %CPU DELTA_TI TIME INTERRUP DAEMON
91 arthas-command-execute system 5 RUNNABLE 1.31 0.002 0:0.142 false true
-1 C1 CompilerThread2 - -1 - 0.9 0.001 0:3.685 false true
-1 VM Periodic Task Thread - -1 - 0.05 0.000 0:6.240 false true
-1 C2 CompilerThread1 - -1 - 0.03 0.000 0:7.726 false true
-1 C2 CompilerThread0 - -1 - 0.01 0.000 0:7.896 false true
2 Reference Handler system 10 WAITING 0.0 0.000 0:0.011 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.020 false true
4 Signal Dispatcher system 9 RUNNABLE 0.0 0.000 0:0.000 false true
- 显示指定线程详情(thread id)
[arthas@31278]$ thread 21
"Druid-ConnectionPool-Create-1961176822" Id=21 WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@7d67b0d8
at sun.misc.Unsafe.park(Native Method)
- waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@7d67b0d8
at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175)
at java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.await(AbstractQueuedSynchronizer.java:2039)
at com.alibaba.druid.pool.DruidDataSource$CreateConnectionThread.run(DruidDataSource.java:2813)
- 显示当前阻塞其他线程的线程 (thread -b)
- thread -i 1000 : 统计最近 1000ms 内的线程 CPU 时间。
- thread -n 3 -i 1000 : 列出 1000ms 内最忙的 3 个线程栈
- thread --state [state] ,查看指定状态的线程 state in (NEW, RUNNABLE, TIMED_WAITING, WAITING, BLOCKED, TERMINATED )
3.2.3 jvm
功能:查看当前 JVM 的信息
- jvm: 查看当前jvm信息,包含
- RUNTIME
- CLASS-LOADING
- COMPIATION
- GARBAGE-COLLECTORS
- MEMORY_MANAGERS
- MEMORY
- OPERATING-SYSTEM
- THREAD
- COUNT: JVM 当前活跃的线程数
- DAEMON-COUNT: JVM 当前活跃的守护线程数
- PEAK-COUNT: 从 JVM 启动开始曾经活着的最大线程数
- STARTED-COUNT: 从 JVM 启动开始总共启动过的线程次数
- DEADLOCK-COUNT: JVM 当前死锁的线程数
- FILE_DESCRIPTOR(文件描述)
- MAX-FILE-DESCRIPTOR-COUNT:JVM 进程最大可以打开的文件描述符数
- OPEN-FILE-DESCRIPTOR-COUNT:JVM 当前打开的文件描述符数
3.2.4 sysprop
功能:查看当前JVM系统属性,也可以修改某个属性
- sysprop:查询所有属性
- sysprop | grep java :查询java命名相关所有属性
[arthas@31278]$ sysprop | grep java
java.specification.v 1.8
java.class.path spring_security_test-0.0.1-SNAPSHOT.jar
java.vm.vendor Oracle Corporation
java.vendor.url http://java.oracle.com/
java.vm.specificatio 1.8
java.home /Library/Java/JavaVirtualMachines/jdk1.8.0_181.jdk/Contents/Home/jre
- sysprop java.version: 查看单个属性
- sysprop user.country CN:修改单个属性
3.2.5 sysenv
功能:查看jvm环境变量
- sysenv: 查询所有环境变量
- sysenv USER : 查询环境变量USER值
3.2.6 vmoption
功能:查看和更新jvm里诊断相关的option
- vmoption : 查看所有的vm option
[arthas@31278]$ vmoption
KEY VALUE ORIGIN WRITEABLE
-------------------------------------------------------------------------------------------------------------
HeapDumpBeforeFullGC false DEFAULT true
HeapDumpAfterFullGC false DEFAULT true
HeapDumpOnOutOfMemoryErro false DEFAULT true
r
HeapDumpPath DEFAULT true
PrintGC false DEFAULT true
- vmoption PrintGC : 查看指定的option
[arthas@31278]$ vmoption PrintGC
KEY VALUE ORIGIN WRITEABLE
-------------------------------------------------------------------------------------------------------------
PrintGC false DEFAULT true
vmoption PrintGC true : 更新指定的option值
[arthas@31278]$ vmoption PrintGC true
Successfully updated the vm option.
NAME BEFORE-VALUE AFTER-VALUE
------------------------------------
PrintGC false true
3.2.7 memory(重要)
功能:查看jvm内存信息
[arthas@31278]$ memory
Memory used total max usage
heap 101M 341M 1820M 5.60%
ps_eden_space 73M 234M 657M 11.13%
ps_survivor_space 0K 10240K 10240K 0.00%
ps_old_gen 28M 97M 1365M 2.11%
nonheap 83M 86M -1 95.95%
code_cache 16M 16M 240M 6.87%
metaspace 59M 61M -1 95.38%
compressed_class_space 7M 8M 1024M 0.74%
direct 0K 0K - 0.00%
mapped 0K 0K - 0.00%
3.2.8 heapdump
功能:类似jmap命令heap dump功能
- heapdump /tmp/dump.hprof : dump到指定目录
- heapdump --live /tmp/dump.hprof : 只dump live对象到指定目录
- heapdump : dump到临时文件
3.2.9 logger
功能:查看logger信息,并可更新logger level
- logger:查看logger信息
- logger -n org.springframework.web : 查看指定包目录/类的logger信息
- logger --name ROOT --level debug : 更新指定logger的level等级
3.2.10 ongl
功能:指定ongl表达式,参考:ognl | arthas
3.3 class/classloader相关
3.3.1 classloader(重要)
功能:查看 classloader 的继承树,urls,类加载信息,使用 classloader 去 getResource
- classloader : 按类加载类型查询统计信息
- classloader -t :查看classloader的继承树
[arthas@31278]$ classloader -t
+-BootstrapClassLoader
+-sun.misc.Launcher$ExtClassLoader@2cdf8d8a
+-com.taobao.arthas.agent.ArthasClassloader@1d11ffe5
+-com.taobao.arthas.agent.ArthasClassloader@10a41d80
+-sun.misc.Launcher$AppClassLoader@55f96302
+-org.springframework.boot.loader.LaunchedURLClassLoader@6267c3bb
Affect(row-cnt:6) cost in 8 ms.
3.3.2 sc
功能:serach class的简写,查看 JVM 已加载的类信息
- sc com.example.spring_security.service.* : 根据包名模糊搜索包路径下所有类
[arthas@31278]$ sc com.example.spring_security.service.*
com.example.spring_security.service.SygPermissionService
com.example.spring_security.service.SygUserService
com.example.spring_security.service.impl.SygPermissionServiceImpl
com.example.spring_security.service.impl.SygUserServiceImpl
- sc -d com.example.spring_security.service.impl.SygPermissionServiceImpl : 打印类的详细信息
- sc -d -f com.example.spring_security.service.impl.SygPermissionS : 打印类的详细信息 + 打印出field信息
[arthas@67574]$ sc -d -f com.example.spring_security.service.impl.SygPermissionS
class-info com.example.spring_security.service.impl.SygPermissionServic
eImpl
code-source file:/Users/##/Downloads/jacoco-0.8.9/lib/spring_sec
urity_test-0.0.1-SNAPSHOT.jar!/BOOT-INF/classes!/
name com.example.spring_security.service.impl.SygPermissionServic
eImpl
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name SygPermissionServiceImpl
modifier public
annotation org.springframework.stereotype.Service
interfaces com.example.spring_security.service.SygPermissionService
super-class +-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@626
7c3bb
+-sun.misc.Launcher$AppClassLoader@55f96302
+-sun.misc.Launcher$ExtClassLoader@2cdf8d8a
classLoaderHash 6267c3bb
fields name LOGGER
type org.slf4j.Logger
modifier final,private,static
value Logger[com.example.spring_security.service.impl.Sy
gPermissionServiceImpl]
name permissionMapper
type com.example.spring_security.dao.SygPermissionMap
per
modifier private
annotation org.springframework.beans.factory.annotation.Aut
owired
3.3.3 sm
功能:search method的简写,查询已加载类的方法信息
- sm java.lang.String : 查询展示类中所有的方法
[arthas@67574]$ sm java.lang.String
java.lang.String <init>([BII)V
java.lang.String <init>([BLjava/nio/charset/Charset;)V
java.lang.String <init>([BLjava/lang/String;)V
java.lang.String equals(Ljava/lang/Object;)Z
java.lang.String toString()Ljava/lang/String;
java.lang.String hashCode()I
java.lang.String compareTo(Ljava/lang/Object;)I
。。。。
java.lang.String copyValueOf([C)Ljava/lang/String;
java.lang.String intern()Ljava/lang/String;
Affect(row-cnt:93) cost in 32 ms.
- sm -d java.lang.String toString : 查询展示方法的详细信息
[arthas@67574]$ sm -d java.lang.String toString
declaring-class java.lang.String
method-name toString
modifier public
annotation
parameters
return java.lang.String
exceptions
classLoaderHash null
Affect(row-cnt:1) cost in 19 ms.
3.3.4 jad(重要)
功能:反编译指定以已加载类的源码,即将 JVM 中实际运行的 class 的 byte code 反编译成 java 代码
- jad java.lang.String : 反编译某个类源码
- jad --source-only java.lang.String : 反编译时只显示源代码,配合mc/retransform命令结合使用
- jad java.lang.String trim : 反编译指定函数
- jad java.lang.String trim --lineNumber false : 反编译时不显示行号
[arthas@67574]$ jad java.lang.String trim --lineNumber false
ClassLoader:
Location:
public String trim() {
int n;
int n2 = this.value.length;
char[] cArray = this.value;
for (n = 0; n < n2 && cArray[n] <= ' '; ++n) {
}
while (n < n2 && cArray[n2 - 1] <= ' ') {
--n2;
}
return n > 0 || n2 < this.value.length ? this.substring(n, n2) : this;
}
Affect(row-cnt:2) cost in 891 ms.
- jad org.apache.log4j.Logger -c 69dcaba4 : 反编译可以指定classloader hashcode,指定classloader加载的类
3.3.5 mc
功能:memory compiler 内存编译器,编辑java文件生成class文件
- mc /tmp/Test.java : 内存编译
- mc -c 327a647b /tmp/Test.java : 指定classloader hashcode编辑
- mc -d /tmp/output /tmp/ClassA.java : 通过-d 参数指定输出目录
3.3.6 retransform
功能:加载外部class文件,加载到jvm中
- retransform -c 6267c3bb /tmp/com/example/spring_security/controller/LoginController.class : 根据指定classloader重新加载类信息
-- 第一步:反编译class文件成java文件
[arthas@67574]$ jad --source-only com.example.spring_security.controller.LoginController --lineNumber false > /tmp/LoginController.java
-- 第二步:编辑LoginController.java文件
-- 第三步:指定classloader编译java文件成class文件
[arthas@67574]$ mc -c 6267c3bb /tmp/LoginController.java -d /tmp/
Memory compiler output:
/tmp/com/example/spring_security/controller/LoginController.class
Affect(row-cnt:1) cost in 1151 ms
-- 第四步:指定classloader重新加载class文件 (加载成功后可以使用jad查询是否更新成功)
[arthas@67574]$ retransform -c 6267c3bb /tmp/com/example/spring_security/controller/LoginController.class
retransform success, size: 1, classes:
com.example.spring_security.controller.LoginController
使用mc命令来编译jad的反编译的代码有可能失败。可以在本地修改代码,编译好后再上传到服务器上。有的服务器不允许直接上传文件,可以使用base64命令来绕过。
-- 第一步:生成类.class文件进行base64编码文件
[arthas@98006]$ base64 --input /Users/**/workspace/spring_security/target/classes/com/example/spring_security/controller/LoginController.class --output /tmp/LoginController_base64.txt
-- 第二步:上传到服务器上
-- 第三步:把base64编码文件反编译成类.class文件
[arthas@98006]$ base64 -d /tmp/LoginController_base64.txt --output /tmp/LoginController.class
-- 第四步:指定classloader重新加载class文件 (加载成功后可以使用jad查询是否更新成功)
[arthas@98006]$ retransform -c 6267c3bb /tmp/LoginController.class
retransform success, size: 1, classes:
com.example.spring_security.controller.LoginController
备注:第一二三步骤可以合并成一步,直接上传本地class文件
- retransform -l : 查看retransform记录列表
[arthas@67574]$ retransform -l
Id ClassName TransformCount LoaderHash LoaderClassName
1 com.example.spr 1 6267c3bb null
ing_security.co
ntroller.LoginC
ontroller
2 com.example.spr 1 6267c3bb null
ing_security.co
ntroller.LoginC
ontroller
- retransform -d 1 : 根据指定id删除retransform
- retransform --deleteAll : 删除所有retransform记录(备注:如果不清除掉所有的 retransform entry,并重新触发 retransform ,则 arthas stop 时,retransform 过的类仍然生效。)
3.4 监控相关
请注意,这些命令,都通过字节码增强技术来实现的,会在指定类的方法中插入一些切面来实现数据统计和观测,因此在线上、预发使用时,请尽量明确需要观测的类、方法以及条件,诊断结束要执行 stop 或将增强过的类执行 reset 命令
3.4.1 monitor
功能:方法执行监控,monitor 命令是一个非实时返回命令,是不断的等待目标 Java 进程返回信息,直到用户输入 Ctrl+C 为止。匹配 class-pattern 类名表达式匹配/method-pattern 方法名表达式匹配/condition-express 条件表达式 的类、方法的调用进行监控。
- monitor com.example.spring_security.controller.LoginController showLogin -n 10 --cycle 10 : -n 表示次数, --cycle / -c 表示周期,单位为秒
[arthas@67574]$ monitor com.example.spring_security.controller.LoginController showLogin -n 10 --cycle 10
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 573 ms, listenerId: 1
timestamp class method tota succe fail avg- fail
l ss rt(m -rat
s) e
--------------------------------------------------------------------------------
2022-11-17 com.example.sprin showLogin 2 2 0 1.73 0.00
15:55:20 g_security.contro %
ller.LoginControl
ler
可使用arthas插件直接生成monitor监控命令
- monitor -c 5 com.example.spring_security.controller.LoginController showLogin "params[0] <= 2" :计算条件表达式过滤统计结果(方法执行完毕之后)
- monitor -b -c 5 com.example.spring_security.controller.LoginController showLogin "params[0] <= 2" :计算条件表达式过滤统计结果(方法执行完毕之前)
3.4.2 stack
功能:输出当前方法被调用的调用路径
- stack com.example.spring_security.controller.LoginController showLogin -n 5 : -n表示统计次数
$ stack demo.MathGame primeFactors
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 36 ms.
ts=2018-12-04 01:32:19;thread_name=main;id=1;is_daemon=false;priority=5;TCCL=sun.misc.Launcher$AppClassLoader@3d4eac69
@demo.MathGame.run()
at demo.MathGame.main(MathGame.java:16)
- stack com.example.spring_security.controller.LoginController showLogin '#cost>5000' : 根据时间过滤,单位毫秒
- 过滤条件包含
- target : the object
- clazz : the object's class
- method : the constructor or method
- params : the parameters array of method
- params[0..n] : the element of parameters array
- returnObj : the returned object of method
- throwExp : the throw exception of method
- isReturn : the method ended by return
- isThrow : the method ended by throwing exception
- #cost : the execution time in ms of method invocation
3.4.3 trace
功能:方法内部调用路径,并输出方法路径上的每个节点上耗时
- trace com.example.spring_security.controller.LoginController showLogin -n 5 --skipJDKMethod false : -n 表示次数; --skipJDKMethod <value> skip jdk method trace, default value true.
[arthas@67574]$ trace com.example.spring_security.controller.LoginController showLogin -n 5 --skipJDKMethod false
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 86 ms, listenerId: 5
---ts=2022-11-17 16:21:24;thread_name=http-nio-9091-exec-7;id=1c;is_daemon=true;priority=5;TCCL=org.springframework.boot.web.embedded.tomcat.TomcatEmbeddedWebappClassLoader@3bb9a3ff
---[1.289843ms] com.example.spring_security.controller.LoginController:showLogin()
`---[20.84% 0.268813ms ] com.example.spring_security.util.LoggerUtil:info() #26
3.4.4 tt(重要)
功能:Time Tunnel,方法执行数据的时空隧道,记录下指定方法每次调用的入参和返回信息,并能对这些不同的时间下调用进行观测
- tt -t com.example.spring_security.controller.LoginController showLogin -n 5 : -t 记录方法的每次执行情况; -n记录次数
[arthas@67574]$ tt -t com.example.spring_security.controller.LoginController showLogin -n 5
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 95 ms, listenerId: 7
IND TIMESTAMP COST( IS- IS OBJECT CLASS METHOD
EX ms) RET -E
XP
--------------------------------------------------------------------------------
100 2022-11-17 0.955 tru fa 0x153e05 LoginController showLogin
0 16:27:57 693 e ls 63
e
100 2022-11-17 0.309 tru fa 0x153e05 LoginController showLogin
1 16:28:09 833 e ls 63
e
参数说明
- INDEX 时间片段记录编号,每一个编号代表着一次调用,后续 tt 还有很多命令都是基于此编号指定记录操作,非常重要。
- TIMESTAMP 方法执行的本机时间,记录了这个时间片段所发生的本机时间
- COST(ms) 方法执行的耗时
- IS-RET 方法是否以正常返回的形式结束
- IS-EXP 方法是否以抛异常的形式结束
- OBJECT 执行对象的hashCode(),注意,曾经有人误认为是对象在 JVM 中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体
- CLASS 执行的类名
- METHOD 执行的方法名
- tt -t *Test print params.length==1 或 tt -t *Test print 'params[1] instanceof Integer' : 可解决方法重载问题
- tt -t *Test print params[0].mobile=="13989838402" 可根据指定参数记录
- tt -l :检索调用记录
- tt -s 'method.name=="showLogin"' : 根据指定的方法名称搜索执行记录
- tt -i 1001 : 查看具体时间片段信息
[arthas@67574]$ tt -i 1001
INDEX 1001
GMT-CREATE 2022-11-17 16:28:09
COST(ms) 0.309833
OBJECT 0x153e0563
CLASS com.example.spring_security.controller.LoginController
METHOD showLogin
IS-RETURN true
IS-EXCEPTION false
RETURN-OBJ @String[login]
Affect(row-cnt:1) cost in 114 ms.
- tt -i 1001 -p : 根据时间片段id重新执行,可通过 --replay-times 指定 调用次数,通过 --replay-interval 指定多次调用间隔(单位 ms, 默认 1000ms)
[arthas@67574]$ tt -i 1001 -p
RE-INDEX 1001
GMT-REPLAY 2022-11-17 16:41:53
OBJECT 0x153e0563
CLASS com.example.spring_security.controller.LoginController
METHOD showLogin
IS-RETURN true
IS-EXCEPTION false
COST(ms) 19.28108
RETURN-OBJ @String[login]
Time fragment[1001] successfully replayed 1 times.
3.4.4 watch(重要)
功能:函数执行数据观测
- watch com.example.spring_security.controller.LoginController showLogin '{params,returnObj,throwExp}' -n 5 -x 3 :-x表示遍历深度,可以调整来打印具体的参数和结果内容,默认值是 1,最大值是4。
[arthas@67574]$ watch com.example.spring_security.controller.LoginController showLogin '{params,returnObj,throwExp}' -n 5 -x 3
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 77 ms, listenerId: 9
method=com.example.spring_security.controller.LoginController.showLogin location=AtExit
ts=2022-11-17 16:55:30; [cost=1.14587ms] result=@ArrayList[
@Object[][isEmpty=true;size=0],
@String[login],
null,
]
- watch 命令定义了 4 个观察事件点,即 -b 函数调用前,-e 函数异常后,-s 函数返回后,-f 函数结束后
- 4 个观察事件点 -b、-e、-s 默认关闭,-f 默认打开,当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出
- watch demo.MathGame primeFactors "{params[0],throwExp}" -e -x 2 : -e 只观测接口异常场景
- watch demo.MathGame primeFactors "{params[0],target}" "params[0]<0" :增加条件表达式,条件为第一个参数小于0
- watch demo.MathGame primeFactors '{params, returnObj}' '#cost>200' -x 2 : 按照耗时来过滤,单位为毫秒
4.arthas idea插件及其使用
下载地址:arthas idea - IntelliJ IDEs Plugin | Marketplace 下载完成后本地安装arthas插件
5.热部署插件
热部署组件支持一键将编辑器中修改的 Java 源码快速编译,并更新到远端应用服务中,免去手动 dump、mc 的过程。此外,也可以一键还原 retransform 的类文件。
热更新(HotSwap)可以在 Java 程序运行时,更新部分 class 文件,而无需重启应用。应用观测器的热部署组件支持一键将编辑器中修改的 Java 源码快速编译,并更新到远端应用服务中,免去构建、打包和重部署的过程,帮助开发者快速验证修复方案的有效性。
适用场景
- 将编辑器内修改的 Java 源代码更新到远端应用里,快速验证修复方法的有效性,而免去耗时的构建、集成和部署过程;
- 对于本地无法搭建运行环境的应用,可以结合应用观测和热部署功能实现远程调试和修复验证。
- Jetbrains 插件获取地址: Alibaba Cloud Toolkit - IntelliJ IDEs Plugin | Marketplace
安装Alibaba Cloud Toolkit
5.1 使用IntelliJ IDEA部署应用到Linux服务器
问题:现有部署流程,需要先本地打包,然后上传到服务器,再执行重启服务脚本,此流程需要在各种运维工具中切换使用,流程比较长并且比较耗时,并且可能出现错误,可以通过Cloud Toolkit一键部署到服务器上!!
启动脚本demo,命名为restartup.sh,存储在/Users/***/Downloads/spring_alibaba_toolkit/目录中,并需要给restartup.sh赋予执行权限,chmod -R 777 restartup.sh
killall java
java -jar /Users/**/Downloads/spring_alibaba_toolkit/spring_security_test-0.0.1-SNAPSHOT.jar
备注:慎用 killall java,需要查询出指定服务进程,然后通过kill查杀指定进程服务!
After deploy启动脚本参数 配置demo:
sh /Users/**/Downloads/spring_alibaba_toolkit/restartup.sh
然后点击RUN即可自动部署服务到指定服务器目录中,并启动服务
添加服务器操作demo
备注:mac电脑需要先开启ssh远程访问,配置地址在共享-远程登录,打开即可; linux可以通过命令开通,自行百度下
5.2 Alibaba Cloud Toolkit使用arthas诊断功能
备注:第一次使用Diagnostic Tools诊断时,会自动安装并初始化Arthas。您需要等待约一分钟后即可开始使用。
然后使用同在服务器上面使用arthas诊断工具操作
备注:通过Aibaba Cloud View标签页面,也可以看出Actions中有多个功能,上传:上传本地文件进服务器;终端:打通终端命令窗口;执行命令:可预设值多条命令,直接选择某条命令直接执行;应用观察:此功能由bug😓;属性:查询链接属性;远端文件:查询服务器上所有文件;诊断:使用arthas诊断工具;移除:删除此ip地址。
5.3 代码监测
Cloud Toolkit集成了Java代码检查功能。该功能基于《阿里巴巴Java开发手册》中的代码规约,执行Java代码的静态检查,帮助您在编译前快速发现代码异常,并提供修改建议。
5.4 热部署
步骤一:进入热部署页面
- 方式一:快捷入口打开方式二:工程中右键打开
- 方式二:工程中右键打开
步骤二:选择目标主机和目标进程进行链接
选择主机和目标进程后,点击链接后进行链接
备注:
1.请确保已安装 JDK 并在 ~/.bashrc 里面配置 JAVA_HOME,此外必须存在 JAVA_HOME/lib/tools.jar
2.热部署不能和arthas服务器同时启动,否则热部署链接目标进程会失败
3.目前功能不支持目标主机为本地服务器地址,如果选择是本地目标主机地址,则选择目标进程为空(同官方人员确认!!!别踩坑,不是你本地配置问题!!!),如下图所示
步骤三:建立连接后,在想要热部署的源码文件上右击(或者直接在类中右击选择热部署),选择“热部署”。(可以先进行热部署,测试是否成功后再改动代码,再次点击热部署改动代码才能生效)
步骤四:部署成功的文件会显示在热部署工具窗口下方,点击名称可以跳转到对应源码文件,点击右侧按钮还原对应文件的全部修改。
步骤五:测试完成后,需要还原热部署源文件,再断开链接,否则热部署文件不会失效(重启之后才会失效热部署代码)!!!
不适用热部署的情况
- 类的继承关系、接口不能改变,包括添加、删除、修改或改变先后顺序等;
- 不能添加、删除、修改类字段、以及字段的访问控制符;
- 不能修改类字段的排列顺序;
- 不能增加、修改或删除方法签名、访问控制符,可以添加或删除 private static 或 private (static) final 方法;
- Open JDK(jdk8u74-b02)之前的版本不能热部署包含 lambda 表达式的源码文件;
- Open JDK(jdk8u74-b02)之后的版本热部署时,不能改变 lambda 表达式在源码文件中出现的顺序;
- 可以添加、删除或修改内部类,但要注意不要改变内部类首次被调用的顺序;
- 不能热部署增加、删除或修改闭包的源码文件。
6.踩到的坑
arthas启动报错
原因:在百度网盘里面下载的jdk安装包,安装包中工具不全导致,需要在官网中下载安装包:https://www.oracle.com/java/technologies/downloads/#java8