Alibaba Arthas
# Arthas 是什么?
Arthas 是 Alibaba 开发得一款 java 诊断工具,可以帮我们查看 jvm 使用情况,查看线程数,排除阻塞线程;监听类和方法得调用次数,失败次数 (失败率),执行时常;监听方法得入参,出参,异常信息;快速反编译查看源码,并修改源码,编译运行;查看堆栈信息等。
# 这里列出一些常用命令并一一讲解
最常用得命令:dashboard,thread,jvm,sc,sm,jad,mc,redefine,monitor,watch。官方地址:https://arthas.aliyun.com/ (opens new window)
# dashboard
dashboard 当前系统的实时数据面板,按 ctrl+c 退出,当运行在 Ali-tomcat 时,会显示当前 tomcat 的实时信息,如 HTTP 请求的 qps, rt, 错误数,线程池信息等等。
数据说明如下:
- ID: Java 级别的线程 ID,注意这个 ID 不能跟 jstack 中的 nativeID 一一对应
- NAME: 线程名
- GROUP: 线程组名
- PRIORITY: 线程优先级,1~10 之间的数字,越大表示优先级越高
- STATE: 线程的状态
- CPU%: 线程消耗的 cpu 占比,采样 100ms,将所有线程在这 100ms 内的 cpu 使用量求和,再算出每个线程的 cpu 使用占比。
- TIME: 线程运行总时间,数据格式为 分:秒
- INTERRUPTED: 线程当前的中断位状态
- DAEMON: 是否是守护线程
# thread
查看当前线程信息,查看线程的堆栈
示例: thread -n 3 找出前 3 个线程的堆栈信息
- id 线程 id;
- -n 指定最忙的前 N 个线程并打印堆栈;
- -b 找出当前阻塞其他线程的线程;
- -i <value> 指定 cpu 占比统计的采样间隔单位为毫秒,建议 5000
$ thread -n 3
"as-command-execute-daemon" Id=58 cpuUsage=76% RUNNABLE
at java.management@12.0.1/sun.management.ThreadImpl.dumpThreads0(Native Method)
at java.management@12.0.1/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:466)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:133)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:79)
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:370)
at java.base@12.0.1/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base@12.0.1/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base@12.0.1/java.lang.Thread.run(Thread.java:835)
Number of locked synchronizers = 1
- java.util.concurrent.ThreadPoolExecutor$Worker@7808db3c
"NioBlockingSelector.BlockPoller-1" Id=24 cpuUsage=17% RUNNABLE (in native)
at java.base@12.0.1/sun.nio.ch.EPoll.wait(Native Method)
at java.base@12.0.1/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:120)
at java.base@12.0.1/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:124)
- locked sun.nio.ch.Util$2@1a574b17
- locked sun.nio.ch.EPollSelectorImpl@7b0d44f5
at java.base@12.0.1/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:136)
at org.apache.tomcat.util.net.NioBlockingSelector$BlockPoller.run(NioBlockingSelector.java:304)
"http-nio-8087-ClientPoller-1" Id=36 cpuUsage=5% RUNNABLE (in native)
at java.base@12.0.1/sun.nio.ch.EPoll.wait(Native Method)
at java.base@12.0.1/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:120)
at java.base@12.0.1/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:124)
- locked sun.nio.ch.Util$2@703ba4c0
- locked sun.nio.ch.EPollSelectorImpl@6a6a92bd
at java.base@12.0.1/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:136)
at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:743)
at java.base@12.0.1/java.lang.Thread.run(Thread.java:835)
Affect(row-cnt:0) cost in 131 ms.
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
示例:thread 打出所有运行线程
$ thread
Threads Total: 37, NEW: 0, RUNNABLE: 14, BLOCKED: 0, WAITING: 17, TIMED_WAITING: 6, TERMINATED: 0
ID NAME GROUP PRIORITY STATE %CPU TIME INTERRUPTED DAEMON
59 as-command-execute-daemon system 10 RUNNABLE 100 0:0 false true
42 AsyncAppender-Worker-arthas-cache.result.AsyncAppender system 9 WAITING 0 0:0 false true
40 Attach Listener system 9 RUNNABLE 0 0:0 false true
14 Catalina-utility-1 main 1 WAITING 0 0:1 false false
15 Catalina-utility-2 main 1 TIMED_WAITING 0 0:0 false false
10 Common-Cleaner InnocuousThreadGroup 8 TIMED_WAITING 0 0:0 false true
39 DestroyJavaVM main 5 RUNNABLE 0 0:12 false false
3 Finalizer system 8 WAITING 0 0:0 false true
19 MQTT Call: mqttId_inbound main 5 WAITING 0 0:0 false false
23 MQTT Ping: mqttId_inbound main 5 TIMED_WAITING 0 0:0 false false
20 MQTT Rec: mqttId_inbound main 5 RUNNABLE 0 0:0 false false
21 MQTT Snd: mqttId_inbound main 5 WAITING 0 0:0 false false
24 NioBlockingSelector.BlockPoller-1 main 5 RUNNABLE 0 0:0 false true
2 Reference Handler system 10 RUNNABLE 0 0:0 false true
4 Signal Dispatcher system 9 RUNNABLE 0 0:0 false true
16 container-0 main 5 TIMED_WAITING 0 0:0 false false
37 http-nio-8087-Acceptor-0 main 5 RUNNABLE 0 0:0 false true
35 http-nio-8087-ClientPoller-0 main 5 RUNNABLE 0 0:0 false true
36 http-nio-8087-ClientPoller-1 main 5 RUNNABLE 0 0:0 false true
25 http-nio-8087-exec-1 main 5 WAITING 0 0:0 false true
34 http-nio-8087-exec-10 main 5 WAITING 0 0:0 false true
26 http-nio-8087-exec-2 main 5 WAITING 0 0:0 false true
27 http-nio-8087-exec-3 main 5 WAITING 0 0:0 false true
28 http-nio-8087-exec-4 main 5 WAITING 0 0:0 false true
29 http-nio-8087-exec-5 main 5 WAITING 0 0:0 false true
30 http-nio-8087-exec-6 main 5 WAITING 0 0:0 false true
31 http-nio-8087-exec-7 main 5 WAITING 0 0:0 false true
32 http-nio-8087-exec-8 main 5 WAITING 0 0:0 false true
33 http-nio-8087-exec-9 main 5 WAITING 0 0:0 false true
44 job-timeout system 9 TIMED_WAITING 0 0:0 false true
45 nioEventLoopGroup-2-1 system 10 RUNNABLE 0 0:0 false false
49 nioEventLoopGroup-2-2 system 10 RUNNABLE 0 0:1 false false
53 nioEventLoopGroup-2-3 system 10 RUNNABLE 0 0:0 false false
46 nioEventLoopGroup-3-1 system 10 RUNNABLE 0 0:0 false false
22 pool-2-thread-4 main 5 WAITING 0 0:0 false false
47 pool-3-thread-1 system 5 TIMED_WAITING 0 0:0 false false
48 pool-4-thread-1 system 5 WAITING 0 0:0 false false
Affect(row-cnt:0) cost in 113 ms.
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
示例:thread 20 查看线程 id 为 20 的堆栈信息
$ thread 20
"MQTT Rec: mqttId_inbound" Id=20 RUNNABLE (in native)
at java.base@12.0.1/java.net.SocketInputStream.socketRead0(Native Method)
at java.base@12.0.1/java.net.SocketInputStream.socketRead(SocketInputStream.java:115)
at java.base@12.0.1/java.net.SocketInputStream.read(SocketInputStream.java:168)
at java.base@12.0.1/java.net.SocketInputStream.read(SocketInputStream.java:140)
at java.base@12.0.1/java.net.SocketInputStream.read(SocketInputStream.java:200)
at java.base@12.0.1/java.io.DataInputStream.readByte(DataInputStream.java:270)
at org.eclipse.paho.client.mqttv3.internal.wire.MqttInputStream.readMqttWireMessage(MqttInputStream.java:92)
at org.eclipse.paho.client.mqttv3.internal.CommsReceiver.run(CommsReceiver.java:133)
at java.base@12.0.1/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
at java.base@12.0.1/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base@12.0.1/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304)
at java.base@12.0.1/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base@12.0.1/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base@12.0.1/java.lang.Thread.run(Thread.java:835)
Number of locked synchronizers = 1
- java.util.concurrent.ThreadPoolExecutor$Worker@79cf8c25
Affect(row-cnt:0) cost in 33 ms.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
示例:thread -b 找出阻塞其他线程的线程
$ thread -b
No most blocking thread found!
Affect(row-cnt:0) cost in 43 ms.
2
3
示例:thread -n 3 -i 5000 阻塞指定 5 秒后收到前 3 线程的堆栈信息
$ thread -n 3 -i 5000
"Catalina-utility-1" Id=14 cpuUsage=21% TIMED_WAITING on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@72b49c8
at java.base@12.0.1/jdk.internal.misc.Unsafe.park(Native Method)
- waiting on java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject@72b49c8
at java.base@12.0.1/java.util.concurrent.locks.LockSupport.parkNanos(LockSupport.java:235)
at java.base@12.0.1/java.util.concurrent.locks.AbstractQueuedSynchronizer$ConditionObject.awaitNanos(AbstractQueuedSynchronizer.java:2123)
at java.base@12.0.1/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:1182)
at java.base@12.0.1/java.util.concurrent.ScheduledThreadPoolExecutor$DelayedWorkQueue.take(ScheduledThreadPoolExecutor.java:899)
at java.base@12.0.1/java.util.concurrent.ThreadPoolExecutor.getTask(ThreadPoolExecutor.java:1054)
at java.base@12.0.1/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1114)
at java.base@12.0.1/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.base@12.0.1/java.lang.Thread.run(Thread.java:835)
"http-nio-8087-ClientPoller-1" Id=36 cpuUsage=14% RUNNABLE (in native)
at java.base@12.0.1/sun.nio.ch.EPoll.wait(Native Method)
at java.base@12.0.1/sun.nio.ch.EPollSelectorImpl.doSelect(EPollSelectorImpl.java:120)
at java.base@12.0.1/sun.nio.ch.SelectorImpl.lockAndDoSelect(SelectorImpl.java:124)
- locked sun.nio.ch.Util$2@703ba4c0
- locked sun.nio.ch.EPollSelectorImpl@6a6a92bd
at java.base@12.0.1/sun.nio.ch.SelectorImpl.select(SelectorImpl.java:136)
at org.apache.tomcat.util.net.NioEndpoint$Poller.run(NioEndpoint.java:743)
at java.base@12.0.1/java.lang.Thread.run(Thread.java:835)
"as-command-execute-daemon" Id=62 cpuUsage=10% RUNNABLE
at java.management@12.0.1/sun.management.ThreadImpl.dumpThreads0(Native Method)
at java.management@12.0.1/sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:466)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:133)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:79)
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:370)
at java.base@12.0.1/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.base@12.0.1/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.base@12.0.1/java.lang.Thread.run(Thread.java:835)
Number of locked synchronizers = 1
- java.util.concurrent.ThreadPoolExecutor$Worker@2d2909be
Affect(row-cnt:0) cost in 5045 ms.
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
# jvm
查看当前 JVM 信息,这里只拿部分信息来说
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
THREAD
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
COUNT 37
DAEMON-COUNT 22
PEAK-COUNT 38
STARTED-COUNT 55
DEADLOCK-COUNT 0
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
FILE-DESCRIPTOR
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
MAX-FILE-DESCRIPTOR-COUNT -1
OPEN-FILE-DESCRIPTOR-COUNT -1
2
3
4
5
6
7
8
9
10
11
12
13
14
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 当前打开的文件描述符数
# sc
查看 JVM 已加载的类信息,
参数说明:lass(包名 + 类名),method(方法名),[d](输出详细信息),[E](开启正则表达式匹配,默认为通配符匹配),[f](输出当前类的成员变量信息(需要配合参数 - d 一起使用)),[x:](指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出)
class-pattern 支持全限定名,如 com.taobao.test.AAA,也支持 com/taobao/test/AAA 这样的格式,这样,我们从异常堆栈里面把类名拷贝过来的时候,不需要在手动把 / 替换为。啦。
示例:sc com.cloud*
$ sc com.cloud*
com.cloud.mqtt.MqttApplication
com.cloud.mqtt.MqttApplication$$EnhancerBySpringCGLIB$$b4fbeb8a
com.cloud.mqtt.mqtt.MqttConfig
com.cloud.mqtt.mqtt.MqttConfig$$EnhancerBySpringCGLIB$$d1953d4e
com.cloud.mqtt.mqtt.MqttConfig$$EnhancerBySpringCGLIB$$d1953d4e$$FastClassBySpringCGLIB$$69e081d8
com.cloud.mqtt.mqtt.MqttConfig$$FastClassBySpringCGLIB$$bf72748c
com.cloud.mqtt.mqtt.MqttConfig$1
com.cloud.mqtt.mqtt.MqttReceiveConfig
com.cloud.mqtt.mqtt.MqttReceiveConfig$$EnhancerBySpringCGLIB$$5631a71d
com.cloud.mqtt.mqtt.MqttSenderConfig
com.cloud.mqtt.mqtt.MqttSenderConfig$$EnhancerBySpringCGLIB$$3bfb2a03
com.cloud.mqtt.mqtt.Publisher
com.cloud.mqtt.web.MqttGateway
com.cloud.mqtt.web.TestController
com.sun.proxy.$Proxy65
Affect(row-cnt:15) cost in 20 ms.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
示例:sc -d -f com.cloud.mqtt.web.TestController 打印类的信息以及字段的信息
$ sc -d -f com.cloud.mqtt.web.TestController
class-info com.cloud.mqtt.web.TestController
code-source file:/home/fengqianrun/opt/server/mqtt-0.0.1.jar!/BOOT-INF/classes!/
name com.cloud.mqtt.web.TestController
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass false
isPrimitive false
isSynthetic false
simple-name TestController
modifier public
annotation org.springframework.web.bind.annotation.RestController,org.springframework.web.bind.annotation.RequestMapping
interfaces
super-class +-java.lang.Object
class-loader +-org.springframework.boot.loader.LaunchedURLClassLoader@439f5b3d
+-jdk.internal.loader.ClassLoaders$AppClassLoader@5bc2b487
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@75d2da2d
classLoaderHash 439f5b3d
fields modifier private
type com.cloud.mqtt.web.MqttGateway
name mqttGateway
annotationjavax.annotation.Resource
Affect(row-cnt:1) cost in 15 ms.
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
# sm
查看已加载类的方法信息,命令只能看到由当前类所声明 (declaring) 的方法,父类则无法看到。
参数说明:class(包名 + 类名),method(方法名称),[d](展示每个方法的详细信息),[E](开启正则表达式匹配,默认为通配符匹配)
示例:sm com.cloud.mqtt.web.TestController 查询 TestController 下的所有方法
$ sm com.cloud.mqtt.web.TestController
com.cloud.mqtt.web.TestController <init>()V
com.cloud.mqtt.web.TestController test(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
com.cloud.mqtt.web.TestController sendMqtt(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
com.cloud.mqtt.web.TestController sendTest(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;
Affect(row-cnt:4) cost in 12 ms.
2
3
4
5
6
示例:sm -d com.cloud.mqtt.web.TestController test 查询 TestController 下的 test 方法详情
$ sm -d com.cloud.mqtt.web.TestController test
declaring-class com.cloud.mqtt.web.TestController
method-name test
modifier public
annotation org.springframework.web.bind.annotation.GetMapping
parameters java.lang.String
java.lang.String
return java.lang.String
exceptions
Affect(row-cnt:1) cost in 13 ms.
2
3
4
5
6
7
8
9
10
11
# jad
jad 反编译指定已加载类得源码,jad 在 Arthas Console 上,反编译出来得源码是带语法高亮得,阅读方便,当然反编译出来得 java 代码可能会存在语法错误,但不影响你的进行阅读。
参数说明:class(包名 + 类名),[c:](类所属 ClassLoader 得 hashcode),[E](开启正则表达匹配,默认通配符)
示例:反编译 com.cloud.mqtt.web.TestController
$ jad com.cloud.mqtt.web.TestController
ClassLoader:
+-org.springframework.boot.loader.LaunchedURLClassLoader@439f5b3d
+-jdk.internal.loader.ClassLoaders$AppClassLoader@5bc2b487
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@75d2da2d
Location:
file:/home/fengqianrun/opt/server/mqtt-0.0.1.jar!/BOOT-INF/classes!/
/*
* Decompiled with CFR 0_132.
*
* Could not load the following classes:
* com.cloud.mqtt.web.MqttGateway
* javax.annotation.Resource
* org.springframework.web.bind.annotation.GetMapping
* org.springframework.web.bind.annotation.RequestMapping
* org.springframework.web.bind.annotation.RequestParam
* org.springframework.web.bind.annotation.RestController
*/
package com.cloud.mqtt.web;
import com.cloud.mqtt.web.MqttGateway;
import javax.annotation.Resource;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping(value={"/test"})
public class TestController {
@Resource
private MqttGateway mqttGateway;
@GetMapping(value={"/"})
public String test(String a, String b) {
String rt = a + b;
return rt;
}
@GetMapping(value={"/send"})
public String sendMqtt(@RequestParam(value="data") String data, @RequestParam(value="topic") String topic) {
String rt = "sendMqtt";
return rt;
}
@GetMapping(value={"/sendTest"})
public String sendTest(@RequestParam(value="data") String data, @RequestParam(value="topic") String topic) {
String rt = "sendMqtt";
return "OK";
}
}
Affect(row-cnt:1) cost in 394 ms.
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
示例:反编译 com.cloud.mqtt.web.TestController test 方法
$ jad com.cloud.mqtt.web.TestController test
ClassLoader:
+-org.springframework.boot.loader.LaunchedURLClassLoader@439f5b3d
+-jdk.internal.loader.ClassLoaders$AppClassLoader@5bc2b487
+-jdk.internal.loader.ClassLoaders$PlatformClassLoader@75d2da2d
Location:
file:/home/fengqianrun/opt/server/mqtt-0.0.1.jar!/BOOT-INF/classes!/
@GetMapping(value={"/"})
public String test(String a, String b) {
String rt = a + b;
return rt;
}
Affect(row-cnt:1) cost in 67 ms.
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# mc
内存编译器,编译.java 文件生成.class 文件。编译生成.class 之后们,可以节后 redefine 命令实现热更新代码。
参数说明:[c:](指定 classloader),[d](指定输出目录)
mc /tmp/Test.java 编译Test.java文件
mc -c 327a647b /tmp/Test.java 指定classloader编译
mc -d /tmp/output /tmp/ClassA.java /tmp/ClassB.java 编译到指定目录
2
3
# redefine
加载外部的 .class 文件到 redefine jvm 已加载的类。
redefine 后的原来的类不能恢复,redefine 有可能失败(比如增加了新的 field),参考 jdk 本身的文档。
参数说明:[c:](ClassLoader 的 hashcode),[p:](外部的.class 文件的完整路径,支持多个)
redefine /tmp/Test.class
结合 jad/mc 命令使用:
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
mc /tmp/UserController.java -d /tmp
redefine /tmp/com/example/demo/arthas/user/UserController.class
- jad命令反编译,然后可以用其它编译器,比如vim来修改源码
- mc命令来内存编译修改过的代码
- 用redefine命令加载新的字节码
2
3
4
5
6
7
8
redefine 的限制,不允许新增加 field/method,正在跑的函数,没有退出不能生效,比如下面新增加的 System.out.println,只有 run () 函数里的会生效。
# monitor
monitor 方法监控执行,是一个非实时得返回命令,意思就是可以一直监控某一个方法,直到你按 ctrl+c 结束为止。
监控得信息包括:timestamp (时间戳),class(java 类),method(方法,包括构造方法和普通方法),total(调用次数),success(成功次数),fail(失败次数),rt(平均 RT),fail-rate(失败率)。
方法参数说明:class(包名 + 类名),method(方法名),[E](开启正则表达匹配,默认通配符),[c:](得到运行报告得间隔时间,默认是 120 秒)
$ monitor -c 5 com.cloud.mqtt.web.TestController test
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 60 ms.
timestamp class method total success fail avg-rt(ms) fail-rate
-------------------------------------------------------------------------------------------------------------
2019-08-19 14:29:03 com.cloud.mqtt.web.TestController test 6 6 0 0.34 0.00%
timestamp class method total success fail avg-rt(ms) fail-rate
-------------------------------------------------------------------------------------------------------------
2019-08-19 14:29:08 com.cloud.mqtt.web.TestController test 1 1 0 0.09 0.00%
timestamp class method total success fail avg-rt(ms) fail-rate
-------------------------------------------------------------------------------------------------------------
2019-08-19 14:29:13 com.cloud.mqtt.web.TestController test 0 0 0 0.00 0.00%
timestamp class method total success fail avg-rt(ms) fail-rate
-------------------------------------------------------------------------------------------------------------
2019-08-19 14:29:18 com.cloud.mqtt.web.TestController test 0 0 0 0.00 0.00%
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
我得类 com.cloud.mqtt.web.TestController ,方法为 test ,-c 5 得意思就是每 5 秒打印一次。
# watch
watch 方法执行数据观测。
参数说明:class(类名表达式匹配),method(方法名表达式匹配),express(观察表达式),condition-express(条件表达式),[b](在方法调用之前观察),[e](在方法异常之后观察),[s](在方法返回之后观察),[f](在方法结束之后 (正常返回和异常返回) 观察),[E](开启正则表达式匹配,默认为通配符匹配),[x:](指定输出结果的属性遍历深度,默认为 1)
特别说明:
- watch 命令定义了 4 个观察事件点,即 -b 方法调用前,-e 方法异常后,-s 方法返回后,-f 方法结束后
- 4 个观察事件点 -b、-e、-s 默认关闭,-f 默认打开,当指定观察点被打开后,在相应事件点会对观察表达式进行求值并输出
- 这里要注意方法入参和方法出参的区别,有可能在中间被修改导致前后不一致,除了 -b 事件点 params 代表方法入参外,其余事件都代表方法出参
- 当使用 -b 时,由于观察事件点是在方法调用前,此时返回值或异常均不存在
curl "http://192.168.15.130:8087/test/?a=1&b=3" ,-x 2的意思是,是否深度解析参数,如果你的参数是一个对象,不深度解析,你看到的只是对象地址,深度解析的值,跟你参数所嵌套层数有关。
$ watch com.cloud.mqtt.web.TestController test "{params,returnObj}" -x 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 41 ms.
ts=2019-08-19 17:02:09; [cost=0.241819ms] result=@ArrayList[
@Object[][
@String[1],
@String[3],
],
@String[13],
]
-b 意思是解析入参,-s解析出参, -n 2执行两次的意思,2次完则结束。结果的输出顺序和事件发生的先后顺序一致,和命令中 -s -b 的顺序无关。
$ watch com.cloud.mqtt.web.TestController test "{params,returnObj}" -x 2 -b -s -n 2
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 46 ms.
ts=2019-08-19 17:06:21; [cost=0.006941ms] result=@ArrayList[
@Object[][
@String[1],
@String[3],
],
null,
]
ts=2019-08-19 17:06:21; [cost=0.945661ms] result=@ArrayList[
@Object[][
@String[1],
@String[3],
],
@String[13],
]
Command execution times exceed limit: 2, so command will exit. You can set it with -n option.
条件表达式,第一个传参大于5才执行
$ watch com.cloud.mqtt.web.TestController test "{params[0],returnObj}" "params[0] > 5"
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 39 ms.
ts=2019-08-19 17:21:58; [cost=0.183399ms] result=@ArrayList[
@String[6],
@String[63],
]
异常信息举例:-e 表示抛出异常时才触发,express中,表示异常信息的变量是throwExp
$ watch demo.MathGame primeFactors "{params[0],throwExp}" -e -x 2
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 62 ms.
ts=2018-12-03 19:38:00; [cost=1.414993ms] result=@ArrayList[
@Integer[-1120397038],
java.lang.IllegalArgumentException: number is: -1120397038, need >= 2
at demo.MathGame.primeFactors(MathGame.java:46)
at demo.MathGame.run(MathGame.java:24)
at demo.MathGame.main(MathGame.java:16),
]
按照耗时进行过滤:#cost>200(单位是ms)表示只有当耗时大于200ms时才会输出,过滤掉执行时间小于200ms的调用
$ watch demo.MathGame primeFactors '{params, returnObj}' '#cost>200' -x 2
$ watch demo.MathGame primeFactors '{params, returnObj}' '#cost>200' -x 2
Press Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 66 ms.
ts=2018-12-03 19:40:28; [cost=2112.168897ms] result=@ArrayList[
@Object[][
@Integer[2141897465],
],
@ArrayList[
@Integer[5],
@Integer[428379493],
],
]
观察当前对象中的属性:如果想查看方法运行前后,当前对象中的属性,可以使用target关键字,代表当前对象,然后使用target.field_name访问当前对象的某个属性
$ watch com.cloud.mqtt.web.TestController test 'target'
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 47 ms.
ts=2019-08-19 17:27:09; [cost=0.450697ms] result=@TestController[
mqttGateway=@$Proxy65[gateway proxy for service interface [interface com.cloud.mqtt.web.MqttGateway]],
]
$ watch com.cloud.mqtt.web.TestController test 'target.mqttGateway'
Press Q or Ctrl+C to abort.
Affect(class-cnt:1 , method-cnt:1) cost in 46 ms.
ts=2019-08-19 17:28:31; [cost=0.352704ms] result=@$Proxy65[
m1=@Method[public boolean java.lang.Object.equals(java.lang.Object)],
m10=@Method[public abstract boolean org.springframework.aop.framework.Advised.isExposeProxy()],
m13=@Method[public abstract void org.springframework.aop.framework.Advised.addAdvisor(org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException],
m7=@Method[public abstract boolean org.springframework.aop.framework.Advised.isProxyTargetClass()],
m15=@Method[public abstract void org.springframework.aop.framework.Advised.removeAdvisor(int) throws org.springframework.aop.framework.AopConfigException],
m23=@Method[public abstract java.lang.Class[] org.springframework.aop.framework.Advised.getProxiedInterfaces()],
m5=@Method[public abstract int org.springframework.aop.framework.Advised.indexOf(org.springframework.aop.Advisor)],
m22=@Method[public abstract org.springframework.aop.TargetSource org.springframework.aop.framework.Advised.getTargetSource()],
m18=@Method[public abstract void org.springframework.aop.framework.Advised.addAdvice(int,org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException],
m19=@Method[public abstract void org.springframework.aop.framework.Advised.addAdvice(org.aopalliance.aop.Advice) throws org.springframework.aop.framework.AopConfigException],
m0=@Method[public native int java.lang.Object.hashCode()],
m24=@Method[public abstract boolean org.springframework.aop.framework.Advised.isInterfaceProxied(java.lang.Class)],
m20=@Method[public abstract boolean org.springframework.aop.framework.Advised.removeAdvice(org.aopalliance.aop.Advice)],
m9=@Method[public abstract void org.springframework.aop.framework.Advised.setExposeProxy(boolean)],
m8=@Method[public abstract void org.springframework.aop.framework.Advised.setTargetSource(org.springframework.aop.TargetSource)],
m2=@Method[public java.lang.String java.lang.Object.toString()],
m26=@Method[public abstract java.lang.Class org.springframework.aop.TargetClassAware.getTargetClass()],
m14=@Method[public abstract void org.springframework.aop.framework.Advised.addAdvisor(int,org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException],
m27=@Method[public abstract java.lang.Class org.springframework.core.DecoratingProxy.getDecoratedClass()],
m16=@Method[public abstract boolean org.springframework.aop.framework.Advised.removeAdvisor(org.springframework.aop.Advisor)],
m4=@Method[public abstract int org.springframework.aop.framework.Advised.indexOf(org.aopalliance.aop.Advice)],
m6=@Method[public abstract boolean org.springframework.aop.framework.Advised.isFrozen()],
m17=@Method[public abstract boolean org.springframework.aop.framework.Advised.replaceAdvisor(org.springframework.aop.Advisor,org.springframework.aop.Advisor) throws org.springframework.aop.framework.AopConfigException],
m11=@Method[public abstract void org.springframework.aop.framework.Advised.setPreFiltered(boolean)],
m3=@Method[public abstract void com.cloud.mqtt.web.MqttGateway.sendToMqtt(java.lang.String,java.lang.String)],
m21=@Method[public abstract java.lang.String org.springframework.aop.framework.Advised.toProxyConfigString()],
m25=@Method[public abstract org.springframework.aop.Advisor[] org.springframework.aop.framework.Advised.getAdvisors()],
m12=@Method[public abstract boolean org.springframework.aop.framework.Advised.isPreFiltered()],
]
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
104
105
106
107
108
109
110