概述
本文主要是个人学习记录,详细教程请前往官网查看
详细教程请前往官网学习https://arthas.aliyun.com/doc/quick-start.html#
测试准备
下载demo应用并启动
# 下载
wget https://arthas.aliyun.com/math-game.jar
# 启动
java -jar math-game.jar
下载Arthas并启动
# 下载Arthas
wget https://arthas.aliyun.com/arthas-boot.jar
# 启动
java -jar arthas-boot.jar
1. 查看JVM信息
1.1 sysprop
可以打印所有的System Properties信息
[arthas@92276]$ sysprop
KEY VALUE
------------------------------------------
java.vendor.url http://java.oracle.com/
java.version 1.8.0_211
user.dir /Users/a10.11.5
......
1.2 sysprop java.version
查看指定参数
[arthas@92276]$ sysprop java.version
KEY VALUE
--------------------------------------------
java.version 1.8.0_211
1.3 jvm
打印jvm详细信息
[arthas@92276]$ jvm
RUNTIME
---------------------------------------------------------------------------------------------------------------------------------------------------------------------
MACHINE-NAME [email protected]
JVM-START-TIME 2022-02-07 14:13:35
MANAGEMENT-SPEC-VERSION 1.2
SPEC-NAME Java Virtual Machine Specification
SPEC-VENDOR Oracle Corporation
SPEC-VERSION 1.8
VM-NAME Java HotSpot(TM) 64-Bit Server VM
VM-VENDOR Oracle Corporation
VM-VERSION 25.211-b12
...
1.4 dashboard
查看当前系统的实时数据面板
[arthas@92276]$ dashboard
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTED DAEMON
-1 C2 CompilerThread0 - -1 - 0.0 0.000 0:3.869 false true
-1 C2 CompilerThread2 - -1 - 0.0 0.000 0:3.743 false true
-1 C2 CompilerThread1 - -1 - 0.0 0.000 0:3.627 false true
33 DestroyJavaVM main 5 RUNNABLE 0.0 0.000 0:3.061 false false
-1 C1 CompilerThread3 - -1 - 0.0 0.000 0:1.788 false true
-1 VM Periodic Task Thread - -1 - 0.0 0.000 0:0.322 false true
46 arthas-NettyHttpTelnetBootstrap-3-2 system 5 RUNNABLE 0.0 0.000 0:0.138 false true
-1 VM Thread - -1 - 0.0 0.000 0:0.096 false true
2. sc/sm 查看已加载的类
2.1 sc
命令可以查找到所有JVM已经加载到的类
如果搜索的是接口,还会搜索所有的实现类
[arthas@92276]$ sc javax.servlet.Filter
com.example.demo.arthas.AdminFilterConfig$AdminFilter
javax.servlet.Filter
org.apache.tomcat.websocket.server.WsFilter
org.springframework.boot.web.filter.OrderedCharacterEncodingFilter
org.springframework.boot.web.filter.OrderedHiddenHttpMethodFilter
org.springframework.boot.web.filter.OrderedHttpPutFormContentFilter
org.springframework.boot.web.filter.OrderedRequestContextFilter
org.springframework.web.filter.CharacterEncodingFilter
org.springframework.web.filter.GenericFilterBean
org.springframework.web.filter.HiddenHttpMethodFilter
org.springframework.web.filter.HttpPutFormContentFilter
org.springframework.web.filter.OncePerRequestFilter
org.springframework.web.filter.RequestContextFilter
org.springframework.web.servlet.resource.ResourceUrlEncodingFilter
Affect(row-cnt:14) cost in 5 ms.
通过-d参数,可以打印出类加载的具体信息
[arthas@92276]$ sc -d javax.servlet.Filter
class-info com.example.demo.arthas.AdminFilterConfig$AdminFilter
code-source file:/Users/a10.11.5/Documents/demo/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/
name com.example.demo.arthas.AdminFilterConfig$AdminFilter
isInterface false
isAnnotation false
isEnum false
isAnonymousClass false
isArray false
isLocalClass false
isMemberClass true
isPrimitive false
isSynthetic false
simple-name AdminFilter
modifier static
annotation
interfaces javax.servlet.Filter
super-class +-java.lang.Object
...
2.2 sm
命令查找类的具体函数
arthas@92276]$ sm com.example.demo.arthas.user.UserController
com.example.demo.arthas.user.UserController ()V
com.example.demo.arthas.user.UserController findUserById(Ljava/lang/Integer;)Lcom/example/demo/arthas/user/User;
Affect(row-cnt:2) cost in 7 ms.
通过-d参数可以打印函数的具体属性
[arthas@92276]$ sm -d com.example.demo.arthas.user.UserController
declaring-class com.example.demo.arthas.user.UserController
constructor-name
modifier public
annotation
parameters
exceptions
classLoaderHash 5b2133b1
declaring-class com.example.demo.arthas.user.UserController
method-name findUserById
modifier public
annotation org.springframework.web.bind.annotation.GetMapping
parameters java.lang.Integer
return com.example.demo.arthas.user.User
exceptions
classLoaderHash 5b2133b1
3. jad反编译类
3.1 jad
命令来反编译代码
[arthas@92276]$ jad com.example.demo.arthas.user.UserController
ClassLoader:
+-org.springframework.boot.loader.LaunchedURLClassLoader@5b2133b1
+-sun.misc.Launcher$AppClassLoader@5c647e05
+-sun.misc.Launcher$ExtClassLoader@452b3a41
Location:
file:/Users/a10.11.5/Documents/demo/demo-arthas-spring-boot.jar!/BOOT-INF/classes!/
package com.example.demo.arthas.user;
import com.example.demo.arthas.user.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping(value={"/user/{id}"})
public User findUserById(@PathVariable Integer id) {
/*15*/ logger.info("id: {}", (Object)id);
/*17*/ if (id != null && id < 1) {
throw new IllegalArgumentException("id < 1");
}
return new User(id.intValue(), "name" + id);
}
}
Affect(row-cnt:1) cost in 128 ms.
3.2 通过--source-only
参数可以只打印出在反编译的源代码
[arthas@92276]$ jad --source-only com.example.demo.arthas.user.UserController
package com.example.demo.arthas.user;
import com.example.demo.arthas.user.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class UserController {
private static final Logger logger = LoggerFactory.getLogger(UserController.class);
@GetMapping(value={"/user/{id}"})
public User findUserById(@PathVariable Integer id) {
/*15*/ logger.info("id: {}", (Object)id);
/*17*/ if (id != null && id < 1) {
throw new IllegalArgumentException("id < 1");
}
return new User(id.intValue(), "name" + id);
}
}
4. Ognl动态执行代码
4.1 Ognl
调用static函数
[arthas@92276]$ ognl '@[email protected]("hello ognl")'
null
4.2 获取类的静态字段
先获取类加载的hashcode,这样能定位到唯一的类
[arthas@92276]$ sc -d com.example.demo.arthas.user.UserController | grep classLoaderHash
classLoaderHash 5b2133b1
[arthas@92276]$ ognl -c 5b2133b1 @com.example.demo.arthas.user.UserController@logger
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas.user]],
childrenList=null,
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
]
还可以通过-x参数控制返回值的展开层数。比如
[arthas@92276]$ ognl -c 5b2133b1 @com.example.demo.arthas.user.UserController@logger -x 2
@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user.UserController],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[
serialVersionUID=@Long[5454405123156820674],
FQCN=@String[ch.qos.logback.classic.Logger],
name=@String[com.example.demo.arthas.user],
level=null,
effectiveLevelInt=@Integer[20000],
parent=@Logger[Logger[com.example.demo.arthas]],
childrenList=@CopyOnWriteArrayList[isEmpty=false;size=1],
aai=null,
additive=@Boolean[true],
loggerContext=@LoggerContext[ch.qos.logback.classic.LoggerContext[default]],
],
...
4.3 执行多行表达式,赋值给临时变量,返回一个List
[arthas@92276]$ ognl '#value1=@System@getProperty("java.home"), #value2=@System@getProperty("java.runtime.name"), {#value1, #value2}'
@ArrayList[
@String[/Library/Java/JavaVirtualMachines/1.8.0_211.jdk/Contents/Home/jre],
@String[Java(TM) SE Runtime Environment],
]
5. watch 监听函数排查异常
5.1 watch
监听函数命令
watch [类名] [方法名] [返回值表达式]
- 第一个参数是类名,支持通配
- 第二个参数是函数名,支持通配
- 第三个参数是返回值表达式:loader,clazz,method,target,params,returnObj,throwExp,isBefore,isThrow,isReturn
示例: 监听方法的参数/返回结果.异常信息, -x
表示结果集展开2层
[arthas@92276]$ watch com.example.demo.arthas.user.UserController * '{params,returnObj,throwExp}' -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 2) cost in 28 ms, listenerId: 5
method=com.example.demo.arthas.user.UserController.findUserById location=AtExit
ts=2022-02-07 15:33:16; [cost=0.345356ms] result=@ArrayList[
@Object[][
@Integer[2],
],
@User[
id=@Integer[2],
name=@String[name2],
],
null,
]
5.2 条件表达式
watch [类名] [方法名] [返回值表达式] [条件表达式]
示例1: 监听方法第一个参数值大于10时打印信息
watch com.example.demo.arthas.user.UserController * '{params,returnObj,throwExp}' 'params[0]>10'
[arthas@92276]$ watch com.example.demo.arthas.user.UserController * '{params,returnObj,throwExp}' 'params[0]>10' -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 2) cost in 29 ms, listenerId: 7
method=com.example.demo.arthas.user.UserController.findUserById location=AtExit
ts=2022-02-07 15:49:29; [cost=0.389882ms] result=@ArrayList[
@Object[][
@Integer[12],
],
@User[
id=@Integer[12],
name=@String[name12],
],
null,
]
示例2: 监听方法抛出异常时打印信息
watch com.example.demo.arthas.user.UserController * '{params,returnObj,throwExp}' -e
[arthas@92276]$ watch com.example.demo.arthas.user.UserController * '{params,returnObj,throwExp}' -e -x 2
Press Q or Ctrl+C to abort.
Affect(class count: 1 , method count: 2) cost in 30 ms, listenerId: 8
method=com.example.demo.arthas.user.UserController.findUserById location=AtExceptionExit
ts=2022-02-07 15:53:58; [cost=0.314462ms] result=@ArrayList[
@Object[][isEmpty=false;size=1],
null,
@IllegalArgumentException[java.lang.IllegalArgumentException: id < 1],
]
示例3: 按照耗时进行过滤
watch com.example.demo.arthas.user.UserController * '{params, returnObj}' '#cost>200'
6. jad/mc/redefine实现动态更新代码
6.1 jad
反编译UserController
jad --source-only com.example.demo.arthas.user.UserController > /tmp/UserController.java
- 把反编译的结果保存到
/tmp/UserController.java
- 修改
/tmp/UserController.java
文件代码然后保存
6.2 sc查找加载UserController的ClassLoader
[arthas@92276]$ sc -d *UserController | grep classLoaderHash
classLoaderHash 5b2133b1
6.3 使用mc(Memory Compiler)命令来把java文件编译成class文件
mc -c 5b2133b1 /tmp/UserController.java -d /tmp
把编译好class的文件保存在/tmp
文件夹下
6.4 redefine命令重新加载新编译好的UserController.class
redefine /tmp/com/example/demo/arthas/user/UserController.class
6.5 检查重新加载的类
jad --source-only com.example.demo.arthas.user.UserController
7. thread查看线程信息
7.1 查看所有线程
[arthas@92276]$ thread
Threads Total: 46, NEW: 0, RUNNABLE: 12, BLOCKED: 0, WAITING: 14, TIMED_WAITING: 5, TERMINATED: 0, Internal threads: 15
ID NAME GROUP PRIORITY STATE %CPU DELTA_TIME TIME INTERRUPTED DAEMON
47 arthas-command-execute system 5 RUNNABLE 0.12 0.000 0:6.418 false true
-1 C1 CompilerThread3 - -1 - 0.07 0.000 0:3.646 false true
-1 VM Periodic Task Thread - -1 - 0.05 0.000 0:4.331 false true
2 Reference Handler system 10 WAITING 0.0 0.000 0:0.009 false true
3 Finalizer system 8 WAITING 0.0 0.000 0:0.041 false true
4 Signal Dispatcher system 9 RUNNABLE 0.0 0.000 0:0.000 false true
34 Attach Listener system 9 RUNNABLE 0.0 0.000 0:0.006 false true
7.2 查看指定线程的栈
[arthas@92276]$ thread 47
"arthas-command-execute" Id=47 RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:448)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processThread(ThreadCommand.java:233)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:120)
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)
7.3 查看CPU使用率前3线程的栈
[arthas@92276]$ thread -n 3
"C1 CompilerThread3" [Internal] cpuUsage=0.23% deltaTime=0ms time=3649ms
"arthas-command-execute" Id=47 cpuUsage=0.15% deltaTime=0ms time=6430ms RUNNABLE
at sun.management.ThreadImpl.dumpThreads0(Native Method)
at sun.management.ThreadImpl.getThreadInfo(ThreadImpl.java:448)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.processTopBusyThreads(ThreadCommand.java:206)
at com.taobao.arthas.core.command.monitor200.ThreadCommand.process(ThreadCommand.java:122)
at com.taobao.arthas.core.shell.command.impl.AnnotatedCommandImpl.process(AnnotatedCommandImpl.java:82)
...
"VM Periodic Task Thread" [Internal] cpuUsage=0.06% deltaTime=0ms time=4459ms
查看5秒内的CPU使用率前3线程栈
thread -n 3 -i 5000
7.4 查找线程是否有阻塞
[arthas@92276]$ thread -b
No most blocking thread found!