梦想还是要有的,万一忘了咋办?

0%

Arthas

目录:

  • 常用指令

线程(thread)

查看主线程

1
thread 1|grep 'main('

当前最忙的N个线程

1
2
3
4
thread -n 3 -i 1000 #列出1000ms内最忙的3个线程
"C1 CompilerThread0" [Internal] cpuUsage=1.63% deltaTime=3ms time=1170ms
"arthas-command-execute" Id=23 cpuUsage=0.11% deltaTime=0ms time=401ms RUNNABLE
"VM Periodic Task Thread" [Internal] cpuUsage=0.07% deltaTime=0ms time=584ms
  • 没有线程ID,包含[Internal]表示为JVM内部线程,参考dashboard命令的介绍。
  • cpuUsage为采样间隔时间内线程的CPU使用率,与dashboard命令的数据一致。
  • deltaTime为采样间隔时间内线程的增量CPU时间,小于1ms时被取整显示为0ms。
  • time 线程运行总CPU时间。

找出当前阻塞其他线程的线程

1
thread -b 

注意, 目前只支持找出synchronized关键字阻塞住的线程, 如果是java.util.concurrent.Lock, 目前还不支持。

查看置顶状态的线程

1
thread -state WAItING|RUNNABLE|BLOCKED|TIMED_WAITING|TERMINATED

虚拟机信息(jvm)

1
jvm

Thread相关

  • COUNT:JVM当前活跃的线程数
  • DEAEMON-COUNt:JVM当前活跃的守护线程数
  • PEAK-COUNT:从JVM启动开始曾经活着的最大线程数
  • STARTED- COUNT:从JVM启动开始总共启动过的线程次数
  • DEADLOCK-COUNT:JVM当前死锁的线程数

文件描述符相关

  • MAX-FiLE-DESCRIPTOR-COUNT:JVM进程最大可以打开的文件描述符
  • OPEN-FILE-DESCRIPTOR-COUNT:JVM当前打开的文件描述符数

日志(logger)

更新日志级别

1
2
3
4
##默认情况下 通过 SystemClassloader执行
logger --name ROOT --level debug|info|error
##war应用、springboot fat jar ,需要制定classloader
logger -c 2a139ss --name ROOT --level debug

查看no appender 的日志

1
logger --include-no-appender 

类信息

查找类

1
2
3
4
5
6
7
8
9
sc class_name 
# 详细信息:
sc -d class_name
# 正则搜索
sc -E regex
# 输出成员变量
sc -d -f class_name
# 变量遍历深度
sc -d -f -x 0 class_name

搜索类的方法信息

1
2
3
4
5
6
sm class_name method_name

# 输出详细信息
sm -d class_name method_name
# 正则搜索
sm -E regex

查看静态属性

1
2
3
4
5
getstatic class_name field_name
# 指定类加载器
getstatic -c 1324j103 class_name field_name
# ognl表达式
getstatic class_name field_name'entrySet().iterator.{? #this.key=="a"}'

堆信息(heapdump)

1
2
3
4
5
6
7
8
# dump到临时文件
heapdump

# 指定文件
heapdump /tmp/dump.hprof

# 只dump live对象
heapdump --live /tmp/dump.hprof

虚拟机工具(vmtool)

强制GC

1
vmtool --action forceGc

获取对象

1
2
3
4
5
6
7
8
vmtool --action getInstances --className java.lang.String --limit 10

# 指定加载器
vmtool --action getInstances --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader --className org.springframework.context.ApplicationContext
# 指定加载器 hashcode
# hashcode 通过sc 查找类获得
vmtool --action getInstances -c 19469ea2 --className org.springframework.context.ApplicationContext

反编译类

1
2
3
4
5
6
7
8
9
jad java.lang.String 
# 只显示源码
jad --source-only java.lang.String
# 指定方法
jad java.lang.String toString
# 不显示行号
jad java.lang.String --lineNumber false
# 指定类加载器
jad -c hashcode java.lang.String

加载类

1
classloader -c 1232131s --load class_name

编译文件

1
2
3
4
5
mc /tmp/Test.java
# 指定类加载器
mc -c xa1324 /tmp/Test.java
# 输出目录
mc -d /tmp/output /tmp/A.java /tmp/B.java

热加载class

1
2
3
4
5
6
7
8
9
# 通过sc查找需要修改的class的ClassLoader
sc -d *OAuthClient |grep classLoaderHash
# jad --source-only xxx.OAuthClient > /tmp/OAuthClient.java
# 修改类文件
vim /tmp/OAuthClient.java
# 重新编译
mc -d /tmp /tmp/OAuthClient.java
# 再使用retransform命令重新加载新编译好的OAuthClient.class
retransform -c 452c5c14 /tmp/OAuthClient.class

观察方法执行数据

可以观察:返回值、抛出异常、入参;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 一直观察方法执行
watch class_name method_name "{params,returnObj,throwExp}"

# 观察一次就自动退出
watch class_name method_name "{params,returnObj,throwExp}" -n 1

# 展开深度
watch class_name method_name "{params,returnObj,throwExp}" -x 2

# 调用前
watch class_name method_name "{params,returnObj}" -b

# 调用前、后
watch class_name method_name "{params,returnObj}" -b -s -n 2

# 增加条件表达式过滤、只有满足条件的才会响应
watch class_name method_name "{params[0],target}" "params[0]<0"

# 只有异常才会出发: -e
watch class_name method_name "{params[0],throwExp}" -e -x 2

观察方法执行记录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# -n 就次数
tt -t class_name method_name -n 3
# 区分重载方法
tt -t *Test print params.length==1
# 指定参数
tt -t *Test print 'params[1] instanceof Integer'
tt -t *Test print params[0].mobile=="13989838402"

# 筛选
tt -s 'methond.name=="primeFactors"'
# 查看调用信息
tt -i 1003
# 重新调用
tt -i 1004 -p