启动与结束
- quit:结束arthas但是不会解除arthas与java服务的绑定
- stop:结束arthas并解除java服务绑定
基本命令的使用
dashboard
thread
- 可以根据列出的线程信息判断具体哪个方法占用的cpu较高
jad
sc
sm
ognl
- 要操作非静态属性或方法前一定是对象已经被new出来了,我们通过一个入口找到这个被创建的对象从而操作对象的属性和方法,就比如Spring中可以通过上下文对象获取到一个指定的对象,从而进行操作,又或者通过ognl表达式直接创建一个对象,然后对这个对象进行操作,又或者使用静态属性对象操作。
- 测试代码:
public class OgnlDemo02 {
public static String s1="s1-public-static-v";
public String s2="s2-public-v";
private String s3="s3-private-v";
public static OgnlDemo02 ognlDemo02=new OgnlDemo02();
public String getS1() {
return OgnlDemo02.s1;
}
public void setS1(String s1) {
OgnlDemo02.s1 = s1;
}
public String getS2() {
return s2;
}
public void setS2(String s2) {
this.s2 = s2;
}
public String getS3() {
return s3;
}
public void setS3(String s3) {
this.s3 = s3;
}
}
-
- 获取非静态属性ognl表达式
- 通过静态属性ognlDemo02对象直接获取公有非静态属性值(这种方法没法获取静态属性)
- ognl '@com.example.OgnlDemo02@ognlDemo02.s2'
- 通过静态属性ognlDemo02对象直接获取私有非静态属性值(这种方法没法获取静态属性)
- 通过静态属性ognlDemo02对象直接获取公有非静态属性值(这种方法没法获取静态属性)
- 通过静态属性ognlDemo02对象的成员方法获取对应属性值
- 获取非静态属性ognl表达式
- 调用非静态方法ognl表达式
- 使用ognl表达式创建一个对象,并对这个对象进行操作
-
public class User { private Long uid; private String nickName; public User(Long uid, String nickName) { this.uid = uid; this.nickName = nickName; } public Long getUid() { return uid; } public void setUid(Long uid) { this.uid = uid; } public String getNickName() { return nickName; } public void setNickName(String nickName) { this.nickName = nickName; } }
-
# 创建一个User对象什么也不做 [arthas@27880]$ ognl 'new com.kerwin.arthas.demo.User(100,"kerwin")' @User[ uid=@Long[100], nickName=@String[kerwin], ] # 创建一个User对象赋值给user,后续可以在别的地方对创建出来的对象进行操作 [arthas@27880]$ ognl '#user = new com.kerwin.arthas.demo.User(100,"kerwin")' @User[ uid=@Long[100], nickName=@String[kerwin], ]
-
[arthas@27880]$ ognl 'new com.kerwin.arthas.demo.User(10001,"kerwin").getNickName()' @String[kerwin] ] [arthas@27880]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin").getNickName()' @String[kerwin] ] [arthas@27880]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),#user.getNickName()' @String[kerwin] ]
-
public class OgnlDemo03 { public static OgnlDemo03 ognlDemo03 = new OgnlDemo03(); private User user; private static User staticUser; private static List<String> lists; private static Map<String,String> maps; public User setUser(User user){ this.user = user; return user; } public static User setStaticUser(User user){ OgnlDemo03.staticUser = user; return user; } public static User getMyUser(){ return new User(10002L,"kerwin2"); } public static User changeUser(User user){ return new User(user.getUid(),user.getNickName()+"---changeUser"); } public static List<String> setLists(List<String> lists){ OgnlDemo03.lists = lists; return lists; } public static Map<String,String> setMaps(Map<String,String> maps){ OgnlDemo03.maps = maps; return maps; } }
-
[arthas@24600]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),@com.kerwin.arthas.demo.OgnlDemo03@setStaticUser(#user)' @User[ uid=@Long[10001], nickName=@String[kerwin], ]
-
[arthas@24600]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),@com.kerwin.arthas.demo.OgnlDemo03@ognlDemo03.setUser(#user)' @User[ uid=@Long[10001], nickName=@String[kerwin], ]
-
[arthas@24600]$ ognl '#user = new com.kerwin.arthas.demo.User(10001,"kerwin"),new com.kerwin.arthas.demo.OgnlDemo03().setUser(#user)' @User[ uid=@Long[10001], nickName=@String[kerwin], ]
-
# {#user1,#user2} 代表将user1、user2这两个对象作为数组输出在控制台,因为这里没有加-x 2默认展开层级为1所以输出的是对象内存地址 [arthas@18904]$ ognl '#user1 = @com.kerwin.arthas.demo.OgnlDemo03@getMyUser(),#user2 = @com.kerwin.arthas.demo.OgnlDemo03@changeUser(#user1),{#user1,#user2}' @ArrayList[ @User[com.kerwin.arthas.demo.User@30833e75], @User[com.kerwin.arthas.demo.User@70ca5419], ] # 加上-x 2 可以展开数组内部对象 [arthas@18904]$ ognl '#user1 = @com.kerwin.arthas.demo.OgnlDemo03@getMyUser(),#user2 = @com.kerwin.arthas.demo.OgnlDemo03@changeUser(#user1),{#user1,#user2}' -x 2 @ArrayList[ @User[ uid=@Long[10002], nickName=@String[kerwin2], ], @User[ uid=@Long[10002], nickName=@String[kerwin2---changeUser], ], ]
-
[arthas@18904]$ ognl '@com.kerwin.arthas.demo.OgnlDemo03@setLists({"k1","k2","k3"})' @ArrayList[ @String[k1], @String[k2], @String[k3], ]
-
[arthas@18904]$ ognl '#map = #{"id":10003L,"nickName":"k3"},@com.kerwin.arthas.demo.OgnlDemo03@setMaps(#map)' @LinkedHashMap[ @String[id]:@Long[10003], @String[nickName]:@String[k3], ]
- 实践操作:使用ognl表达式获取Spring上下文中对象,并进行操作
-
@Service public class OgnlDemoService { private String description; public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
-
@Component public class SpringApplicationContext implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { SpringApplicationContext.applicationContext = applicationContext; } /** * 通过class获取Bean */ public static <T> T getBean(Class<T> clazz) { return applicationContext.getBean(clazz); } /** * 通过name获取 Bean. */ public static Object getBean(String name) { return applicationContext.getBean(name); } }
-
# 通过beanName获取 [arthas@30000]$ ognl '@com.example.test_mybatis.controller.SpringApplicationContext@getBean("ognlDemoService")' @OgnlDemoService[ description=null, ] # 通过class获取 [arthas@30000]$ ognl '#OgnlDemoServiceClass =@com.example.test_mybatis.service.OgnlDemoService@class,@com.kerwin.arthas.utils.Sp ringApplicationContext@getBean(#OgnlDemoServiceClass)' @OgnlDemoService[ description=null, ]
-
[arthas@30000]$ ognl '@com.example.test_mybatis.controller.SpringApplicationContext@getBean("ognlDemoService").description' null [arthas@30000]$ ognl '@com.example.test_mybatis.controller.SpringApplicationContext@getBean("ognlDemoService").setDescription("HelloW orld")' null [arthas@30000]$ ognl '@com.example.test_mybatis.controller.SpringApplicationContext@getBean("ognlDemoService").getDescription()' @String[Hello World]
-
trace
- 使用 trace 命令可以跟踪统计方法耗时,经常用于排查运行较慢、耗时较长的场景
- 测试代码
-
package com.example.test_mybatis.controller; import com.example.test_mybatis.service.UserServiceImpl; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; @RestController @Slf4j public class UserController { @Autowired private UserServiceImpl userService; @GetMapping(value = "/user") public HashMap<String, Object> getUser(Integer uid) throws Exception { // 模拟用户查询 userService.get(uid); HashMap<String, Object> hashMap = new HashMap<>(); hashMap.put("uid", uid); hashMap.put("name", "name" + uid); return hashMap; } } package com.example.test_mybatis.service; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; @Service @Slf4j public class UserServiceImpl { public void get(Integer uid) throws Exception { check(uid); service(uid); redis(uid); mysql(uid); } public void service(Integer uid) throws Exception { int count = 0; for (int i = 0; i < 10; i++) { count += i; } log.info("service end {}", count); } public void redis(Integer uid) throws Exception { int count = 0; for (int i = 0; i < 10000; i++) { count += i; } log.info("redis end {}", count); } public void mysql(Integer uid) throws Exception { long count = 0; for (int i = 0; i < 10000000; i++) { count += i; } log.info("mysql end {}", count); } public boolean check(Integer uid) throws Exception { if (uid == null || uid < 0) { log.error("uid不正确,uid:{}", uid); throw new Exception("uid不正确"); } return true; } }
- 在arthas中使用命令 trace com.example.test_mybatis.controller.UserController getUser
- 可以开启对这个方法的监控,当请求“/get”之后arthas会打印方法中的耗时情况。
- 但是一般只有业务方法才会监控,可以增加--skipJDKMethod false后就可以监控jdk中的函数了(将属性放在trace后面)
- 很清楚的看到是 com.UserServiceImpl的 mysql方法耗时是最高的。
-
monitor
- 使用 monitor 命令监控统计方法的执行情况。
- 每5秒统计一次 com.UserServiceImpl 类的 get 方法执行情况。
- monitor -c 5 com.UserServiceImpl get
-
watch
- 观察方法信息
- 常用方法
-
# 查看入参和出参 $ watch com.Arthas addHashSet '{params[0],returnObj}' # 查看入参和出参大小 $ watch com.Arthas addHashSet '{params[0],returnObj.size}' # 查看入参和出参中是否包含 'count10' $ watch com.Arthas addHashSet '{params[0],returnObj.contains("count10")}' # 查看入参和出参,出参 toString $ watch com.Arthas addHashSet '{params[0],returnObj.toString()}'
- 可以对传入的参数进行操作
-
stack
- 使用stack查看方法的调用情况
tt
- 是否异常、是否有返回、消耗的时间