Arthas常用高级用法
视频地址:
获取Spring Bean
tt命令
-
场景
- 执行Spring中某个Bean的特定方法
- 对某段代码做测试,但当前方法不能直接被接口访问
- Dubbo Service中的代码,不想使用telent invoke调用
- 线上观察Bean内存中的数据
-
释义
- 理解为
watch
命令的变体,watch
命令是实时监听,而tt
命令可以将观测到的数据记录下来
- 理解为
-
使用示例
-
记录任意地址的调用
tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod -n 1
-n
指定记录次数,否则可能导致OOM- 直接访问
localhost:应用端口
即可触发 - 监听的是Spring MVC中的处理适配器,所有接口调用都会被触发
- 成功后,会退出
tt
命令的监听,并输出一个记录ID,第一次记录为1000
-
获取Spring容器并调用方法
tt -i 1000 -w 'target.getApplicationContext().getBean("testBean").test()'
- 使用
-w
以及OGNL表达式获取记录的目标对象并调用目标对象的getApplicationContext
方法 - 获取到Spring容器后,调用
getBean
方法,填入BeanName,后面输入要调用的方法
- 使用
-
命令别名
-
问题
- 命令太长记不住
- 每次都要打开文档查看命令
-
解决方案
- 给命令取一个别名,输入别名直接替换为命令
- 使用AutoHotKey(V2)创建别名
-
别名示例
att
(记录调用)::att::tt -t org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter invokeHandlerMethod -n 1
agb
(参数1:BeanName;参数2:tt索引,默认值为1000)::agb:: { args := InputBox("请输入tt -i 参数", "tt -i [number:1000] -w 'target.getApplicationContext().getBean(beanName)'", "w520 h100").value tti := "1000" SplitArgs := StrSplit(args, " ") if (SplitArgs.Length >= 2) tti := SplitArgs[2] newArgs := 'tt -i ' tti ' -w `'target.getApplicationContext().getBean("' SplitArgs[1] '\`")\`'' Send newArgs }
-
使用方法
- 创建
aa.ank
文件,将上述脚本内容复制进去,保存退出 - 给
aa.ank
文件创建快捷方式,移动到C:\Users\你的用户名\AppData\Roaming\Microsoft\Windows\Start Menu\Programs\Startup
- 创建
调用静态方法
-
OGNL表达式
- 被调用者为静态
ognl '@java.util.Objects@hashCode(1)' ognl '@java.lang.Thread@currentThread()' ognl '@java.util.ArrayList@DEFAULT_CAPACITY'
- 被调用者为实例
ognl '@java.lang.System@out.println("hello world")' ognl '@java.lang.Runtime@getRuntime().gc()'
- 被调用者为静态
-
规则
- 访问类:
@全类名
- 例:
@java.lang.System
- 例:
- 访问类对象:
@全类名@class
- 例:
@java.lang.System@class
- 例:
- 静态字段(方法):
@静态字段(方法)
- 例:
ognl '@java.lang.Thread@currentThread()
- 例:
- 实例字段(方法):
.实例字段(方法)
- 例:
ognl '@java.lang.Thread@currentThread().name
- 例:
- 访问类:
参数为对象类型的方法调用
-
痛点
string
类型和数字类型可以直接传- 但是参数为对象接口不可以直接传参,并且也不能支持JSON写法
-
场景
- 使用
tt
命令调用方法参数为对象时 - 使用OGNL调用静态方法时
- 使用
-
解决方案
- 使用JSON库将JSON字符串解析为实体对象
@com.alibaba.fastjson.JSON@parseObject("{\\"name\\":\\"a\\",\\"age\\":1}", @com.maple.spring.Student@class)
- 示例
tt -i 1000 -w 'target.getApplicationContext().getBean("springBootApplication").test03(@com.alibaba.fastjson.JSON@parseObject("{\\"name\\":\\"name_babb0dc2c3cb\\",\\"age\\":1}", @com.maple.spring.Student@class))'
- 注意:是否有getter和setter方法
- 使用JSON库将JSON字符串解析为实体对象
查看内存数据
- vmtool
- 通过类型找到内存对象(谨慎使用)
- 获取对象
vmtool --action getInstances --className com.maple.spring.SpringBootApplication --limit 1
- 执行方法
vmtool --action getInstances --className com.maple.spring.SpringBootApplication --limit 1 --express 'instances[0].test03(null)'
找出忙碌线程
- thread命令
- 输出最忙的前3个线程并打印堆栈
thread -n 3
- 输出最忙的前3个线程并打印堆栈