查询静态变量
[arthas@174072]$ getstatic com.mulang.octopus.Main standAloneStorageNode
field: standAloneStorageNode
@Boolean[true]
Affect(row-cnt:1) cost in 6 ms.
使用ognl方式
[arthas@227877]$ ognl "@java.nio.Bits@RESERVED_MEMORY"
@AtomicLong[
serialVersionUID=@Long[1927816293512124184],
VM_SUPPORTS_LONG_CAS=@Boolean[true],
U=@Unsafe[jdk.internal.misc.Unsafe@12b9a7fa],
VALUE=@Long[16],
value=@Long[9515349],
serialVersionUID=@Long[-8742448824652078965],
]
[arthas@227877]$
[arthas@227877]$ ognl "@java.nio.Bits@RESERVED_MEMORY.get()"
@Long[9515349]
[arthas@227877]$
调用没有参数的静态命令.
#public static DataStoreType typeDataStore()
[arthas@174072]$ ognl '@com.mulang.octopus.blockstore.BlockServiceProxy@typeDataStore()'
@DataStoreType[LOCAL_DISK]
调用有参数的静态命令.
#public static long diskFreeCapacity(int serverId, int diskId) throws IOException
[arthas@11123]$ ognl '@com.mulang.octopus.blockstore.BlockServiceProxy@diskFreeCapacity(3,40001)'
@Long[211085709312]
解决方法重载
通过制定参数个数的形式解决不同的方法签名
tt -t *Test print params.length\=\=1
通过制定参数个数的形式解决不同的方法签名,如果参数个数一样,你还可以这样写
tt -t *Test print ‘params[1] instanceof Integer’
解决指定参数
tt -t *Test print params[0].mobile\=\=”13989838402″
构成条件表达式的 Advice 对象
public class Advice {
private final ClassLoader loader; //本次调用类所在的 ClassLoader
private final Class<?> clazz; //本次调用类的 Class 引用
private final ArthasMethod method; //本次调用方法反射引用
private final Object target; //本次调用类的实例
//本次调用参数列表,这是一个数组,如果方法是无参方法则为空数组
private final Object[] params;
//本次调用返回的对象。当且仅当 isReturn==true 成立时候有效,表明方法调用是以正常返回的方式结束。如果当前方法无返回值 void,则值为 null
private final Object returnObj;
//本次调用抛出的异常。当且仅当 isThrow==true 成立时有效,表明方法调用是以抛出异常的方式结束。
private final Throwable throwExp;
//辅助判断标记,当前的通知节点有可能是在方法一开始就通知,此时 isBefore==true 成立,同时 isThrow==false 和 isReturn==false,因为在方法刚开始时,还无法确定方法调用将会如何结束。
private final boolean isBefore;
//辅助判断标记,当前的方法调用以抛异常的形式结束。
private final boolean isThrow;
//辅助判断标记,当前的方法调用以正常返回的形式结束。
private final boolean isReturn;
// getter/setter
}
所有变量都可以在表达式中直接使用,如果在表达式中编写了不符合 OGNL 脚本语法或者引入了不在表格中的变量,则退出命令的执行;
热更新某个类
class/classloader 相关
- classloader – 查看 classloader 的继承树,urls,类加载信息,使用 classloader 去 getResource
- dump – dump 已加载类的 byte code 到特定目录
- jad – 反编译指定已加载类的源码
- mc – 内存编译器,内存编译.java文件为.class文件
- redefine – 加载外部的.class文件,redefine 到 JVM 里
- retransform – 加载外部的.class文件,retransform 到 JVM 里
- sc – 查看 JVM 已加载的类信息
- sm – 查看已加载类的方法信息
$ retransform /tmp/MathGame.class
retransform success, size: 1, classes:
demo.MathGame
Note:
- redefine 的 class 不能修改、添加、删除类的 field 和 method,包括方法参数、方法名称及返回值
- 如果 mc 失败,可以在本地开发环境编译好 class 文件,上传到目标系统,使用 redefine 热加载 class
tt 和 watch
- 需要强调的是,tt 命令是将当前环境的对象引用保存起来,但仅仅也只能保存一个引用而已。如果方法内部对入参进行了变更,或者返回的对象经过了后续的处理,那么在 tt 查看的时候将无法看到当时最准确 的值。这也是为什么 watch 命令存在的意义
表格字段 | 字段解释 |
---|---|
INDEX | 时间片段记录编号,每一个编号代表着一次调用,后续 tt 还有很多命令都是基于此编号指定记录操作,非常重要。 |
TIMESTAMP | 方法执行的本机时间,记录了这个时间片段所发生的本机时间 |
COST(ms) | 方法执行的耗时 |
IS-RET | 方法是否以正常返回的形式结束 |
IS-EXP | 方法是否以抛异常的形式结束 |
OBJECT | 执行对象的hashCode(),注意,曾经有人误认为是对象在 JVM 中的内存地址,但很遗憾他不是。但他能帮助你简单的标记当前执行方法的类实体 |
CLASS | 执行的类名 |
METHOD | 执行的方法名 |
[arthas@15217]$ tt -t demo.MathGame primeFactors -n 3
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 100 ms, listenerId: 1
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1000 2023-03-10 03:37:42 1.419275 true false 0x232204a1 MathGame primeFactors
1001 2023-03-10 03:37:43 0.144813 false true 0x232204a1 MathGame primeFactors
1002 2023-03-10 03:37:44 4.481957 true false 0x232204a1 MathGame primeFactors
Command execution times exceed limit: 3, so command will exit. You can set it with -n option.
[arthas@15217]$ tt -t demo.MathGame primeFactors -n 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 1) cost in 28 ms, listenerId: 2
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1003 2023-03-10 03:38:15 0.128059 true false 0x232204a1 MathGame primeFactors
1004 2023-03-10 03:38:16 0.028727 true false 0x232204a1 MathGame primeFactors
Command execution times exceed limit: 2, so command will exit. You can set it with -n option.
#list
[arthas@15217]$ tt -l
INDEX TIMESTAMP COST(ms) IS-RET IS-EXP OBJECT CLASS METHOD
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1000 2023-03-10 03:37:42 1.419275 true false 0x232204a1 MathGame primeFactors
1001 2023-03-10 03:37:43 0.144813 false true 0x232204a1 MathGame primeFactors
1002 2023-03-10 03:37:44 4.481957 true false 0x232204a1 MathGame primeFactors
1003 2023-03-10 03:38:15 0.128059 true false 0x232204a1 MathGame primeFactors
1004 2023-03-10 03:38:16 0.028727 true false 0x232204a1 MathGame primeFactors
Affect(row-cnt:5) cost in 3 ms.
[arthas@15217]$ tt -i 1003
INDEX 1003
GMT-CREATE 2023-03-10 03:38:15
COST(ms) 0.128059
OBJECT 0x232204a1
CLASS demo.MathGame
METHOD primeFactors
IS-RETURN true
IS-EXCEPTION false
PARAMETERS[0] @Integer[162701]
RETURN-OBJ @ArrayList[
@Integer[7],
@Integer[11],
@Integer[2113],
]
Affect(row-cnt:1) cost in 4 ms.
#replay
[arthas@15217]$ tt -i 1003 -p
RE-INDEX 1003
GMT-REPLAY 2023-03-10 04:37:14
OBJECT 0x232204a1
CLASS demo.MathGame
METHOD primeFactors
PARAMETERS[0] @Integer[162701]
IS-RETURN true
IS-EXCEPTION false
COST(ms) 0.047237
RETURN-OBJ @ArrayList[
@Integer[7],
@Integer[11],
@Integer[2113],
]
Time fragment[1003] successfully replayed 1 times.