Arthas--基础--3.8--常用命令--sc、sm、jad、mc、retransform、dump、classloader
·
Arthas–基础–3.8–常用命令–sc、sm、jad、mc、retransform、dump、classloader
1、介绍
| 命令 | 说明 |
|---|---|
sc |
Search Class 查看运行中的类信息 |
sm |
Search Method 查看类中方法信息 |
jad |
反编译字节码为源代码 |
mc |
Memory Compile 将源代码编译成字节码 |
retransform |
将编译好的字节码文件加载到JVM中运行 |
| dump | 保存已加载字节码文件 |
| classloader | 获取类加载器信息 |
2、sc:查看类信息
sc:"Search-Class"的简写。
查看JVM已加载的类信息
- 这个命令能搜索出所有已经加载到 JVM 中的 Class 信息
- sc 默认开启了子类匹配功能,也就是说所有当前类的子类也会被搜索出来,想要精确的匹配,请打开
options disable-sub-class true开关。
2.1、参数
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配,class-pattern 支持全限定名,如 com.taobao.test.AAA,也支持 com/taobao/test/AAA 这样的格式,这样,我们从异常堆栈里面把类名拷贝过来的时候,不需要在手动把/替换为.啦。 |
| method-pattern | 方法名表达式匹配 |
| [d] | 输出当前类的详细信息,包括这个类所加载的原始文件来源、类的声明、加载的 ClassLoader 等详细信息。 如果一个类被多个 ClassLoader 所加载,则会出现多次 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
| [f] | 输出当前类的成员变量信息(需要配合参数-d 一起使用) |
| [x:] | 指定输出静态变量时属性的遍历深度,默认为 0,即直接使用 toString 输出 |
[c:] |
指定 class 的 ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
[n:] |
具有详细信息的匹配类的最大数量(默认为 100) |
[cs <arg>] |
指定 class 的 ClassLoader#toString() 返回值。长格式[classLoaderStr <arg>] |
2.2、测试
# 模糊搜索demo包下所有的类
sc demo.*
[arthas@9668]$ sc demo.*
demo.MathGame
# 打印类的详细信息
sc -d demo.MathGame

3、sm:查看已加载方法信息
- "Search-Method"的简写
- 只能看到由当前类所声明 (declaring) 的方法,父类则无法看到
3.1、参数
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配 |
| method-pattern | 方法名表达式匹配 |
| [d] | 展示每个方法的详细信息 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
[c:] |
指定 class 的 ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
[n:] |
具有详细信息的匹配类的最大数量(默认为 100) |
3.2、测试
# 显示String类加载的方法
sm java.lang.String
# 查看方法信息
sm demo.MathGame
# 查看方法详细信息
sm -d demo.MathGame
3.2.1、显示String类加载的方法
# 显示String类加载的方法
sm java.lang.String

3.2.2、查看方法详细信息
# 查看方法详细信息
sm -d demo.MathGame

4、jad:反编译已加载类源码
-
将class文件java 代码
-
反编译出来的 java 代码可能会存在语法错误,但不影响你进行阅读理解
4.1、参数说明
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配 |
[c:] |
类所属 ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
4.2、测试
# 反编译MathGame方法
jad demo.MathGame
# 反编绎时只显示源代码(排除ClassLoader信息)。
# 默认情况下,反编译结果里会带有ClassLoader信息,通过--source-only选项,可以只打印源代码。方便和mc/redefine命令结合使用。
jad --source-only demo.MathGame
# 反编译到指定文件中
jad --source-only demo.MathGame > Hello.java
# 只反编译mathGame类型中main方法
jad demo.MathGame main

5、mc:编译Java代码
- 内存编译
- 将
.java文件编译为.class
# 在内存中编译Hello.java
mc /root/arthas/Hello.java
# 通过-d命令指定输出目录
mc -d /root/arthas/ /root/arthas/Hello.java
# 可以通过-c参数指定 classloader
mc -c 327a647b /tmp/Test.java
# 可以通过--classLoaderClass参数指定 ClassLoader:
$ mc --classLoaderClass org.springframework.boot.loader.LaunchedURLClassLoader /tmp/UserController.java -d /tmp
5.1、准备
cat > /root/arthas/Hello.java << EOF
public class Hello {
public static void main(String[] args) {
System.out.println("hello");
}
}
EOF
5.2、测试
# 通过-d命令指定输出目录
mc -d /root/arthas/ /root/arthas/Hello.java
[arthas@9711]$ mc -d /root/arthas/ /root/arthas/Hello.java
Memory compiler output:
/root/arthas/Hello.class
Affect(row-cnt:1) cost in 1268 ms.
6、retransform(转化)
-
加载外部的
.class文件 -
retransform jvm 已加载的类。
-
加载指定的 .class 文件,然后解析出 class name,再 retransform jvm 中已加载的对应的类。每加载一个
.class文件,则会记录一个 retransform entry. -
如果多次执行 retransform 加载同一个 class 文件,则会有多条 retransform entry.
-
retransform /tmp/Test.class
retransform -l
retransform -d 1 # delete retransform entry
retransform --deleteAll # delete all retransform entries
retransform --classPattern demo.* # triger retransform classes
retransform -c 327a647b /tmp/Test.class /tmp/Test\$Inner.class
retransform --classLoaderClass 'sun.misc.Launcher$AppClassLoader' /tmp/Test.class
6.1、准备
上传一个项目,核心代码如下

6.2、测试
6.2.1、修改代码
获取源码
# 获取源码
jad --source-only fei.zhou.demo1.business.controller.Demo1Controller > /root/arthas/demo/Demo1Controller.java
编辑源码
vim /root/arthas/demo/Demo1Controller.java

反编译为class文件
反编译为class文件
mc /root/arthas/demo/Demo1Controller.java -d /root/arthas/demo/
# 使用mc命令来编译jad的反编译的代码有可能失败,可以在本地修改代码,编译好后再上传到服务器上
[arthas@9937]$ mc /root/arthas/demo/Demo1Controller.java -d /root/arthas/demo/
Memory compiler error, exception message: Compilation Error
message: 找不到符号
符号: 类 Demo1Service
位置: 程序包 fei.zhou.demo1.business.controller , line: 13 ,
message: 程序包javax.servlet.http不存在 , line: 14 ,
message: 程序包org.springframework.beans.factory.annotation不存在 , line: 15 ,
message: 程序包org.springframework.web.bind.annotation不存在 , line: 16 ,
message: 程序包org.springframework.web.bind.annotation不存在 , line: 17 ,
message: 找不到符号
符号: 类 RestController , line: 19 ,
message: 找不到符号
符号: 类 Demo1Service
位置: 类 fei.zhou.demo1.business.controller.Demo1Controller , line: 22 ,
message: 找不到符号
符号: 类 HttpServletRequest
位置: 类 fei.zhou.demo1.business.controller.Demo1Controller , line: 25 ,
message: 找不到符号
符号: 类 Autowired
位置: 类 fei.zhou.demo1.business.controller.Demo1Controller , line: 21 ,
message: 找不到符号
符号: 类 RequestMapping
位置: 类 fei.zhou.demo1.business.controller.Demo1Controller , line: 24 ,
, please check $HOME/logs/arthas/arthas.log for more details.
我使用本地编译好代码,再上传到机器去


加载class文件到内存中
retransform /root/arthas/demo/Demo1Controller.class

- 加载指定的 .class 文件,然后解析出 class name,再 retransform jvm 中已加载的对应的类。每加载一个
.class文件,则会记录一个 retransform entry. - 如果多次执行 retransform 加载同一个 class 文件,则会有多条 retransform entry.
请求测试

6.3、其他功能
6.3.1、查看 retransform entry
$ retransform -l

- TransformCount 统计在 ClassFileTransformer#transform 函数里尝试返回 entry 对应的 .class 文件的次数,但并不表明 transform 一定成功。
6.3.2、删除指定 retransform entry
需要指定 id:
retransform -d 1
6.3.3、删除所有 retransform entry
retransform --deleteAll
6.3.4、显式触发 retransform
$ retransform --classPattern demo.MathGame
retransform success, size: 1, classes:
demo.MathGame
注意:对于同一个类,当存在多个 retransform entry 时,如果显式触发 retransform ,则最后添加的 entry 生效(id 最大的)。
6.3.5、消除 retransform 的影响
如果对某个类执行 retransform 之后,想消除影响,则需要:
- 删除这个类对应的 retransform entry
- 重新触发 retransform
# 删除这个类对应的 retransform entry
retransform -d 1
# 重新触发 retransform
retransform --classPattern fei.zhou.demo1.business.controller.Demo1Controller

提示
如果不清除掉所有的 retransform entry,并重新触发 retransform ,则 arthas stop 时,retransform 过的类仍然生效。
6.4、retransform 的限制
- 不允许新增加 field/method
- 正在跑的函数,没有退出不能生效,比如下面新增加的
System.out.println,只有run()函数里的会生效
public class MathGame {
public static void main(String[] args) throws InterruptedException {
MathGame game = new MathGame();
while (true) {
game.run();
TimeUnit.SECONDS.sleep(1);
// 这个不生效,因为代码一直跑在 while里
System.out.println("in loop");
}
}
public void run() throws InterruptedException {
// 这个生效,因为run()函数每次都可以完整结束
System.out.println("call run()");
try {
int number = random.nextInt();
List<Integer> primeFactors = primeFactors(number);
print(number, primeFactors);
} catch (Exception e) {
System.out.println(String.format("illegalArgumentCount:%3d, ", illegalArgumentCount) + e.getMessage());
}
}
}
7、dump:保存已加载字节码文件
- 将已加载类的字节码文件保存在特定目录
- 适用场景:批量下载指定包目录的 class 字节码
7.1、 参数
| 参数名称 | 参数说明 |
|---|---|
| class-pattern | 类名表达式匹配 |
[c:] |
类所属 ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
[d:] |
设置类文件的目标目录 |
| [E] | 开启正则表达式匹配,默认为通配符匹配 |
7.2、测试
# 把String类的字节码文件保存到~/logs/arthas/classdump/目录下
dump java.lang.String
# -d 指定保存目录
dump -d /tmp/output java.lang.String
# 把demo包下所有的类的字节码文件保存到~/logs/arthas/classdump/目录下
dump demo.*
# -c 指定 classLoader,注意 hashcode 是变化的,需要先查看当前的 ClassLoader 信息,提取对应 ClassLoader 的 hashcode。
dump -c 3d4eac69 demo.*
# 对于只有唯一实例的 ClassLoader 可以通过--classLoaderClass指定 class name,使用起来更加方便
dump --classLoaderClass sun.misc.Launcher$AppClassLoader demo.*

8、classloader:获取类加载器信息
-
将 JVM 中所有的classloader的信息统计出来,并可以展示继承树,urls等。
-
可以让指定的classloader去getResources,打印出所有查找到的resources的url。对于ResourceNotFoundException异常比较有用。
-
显示所有类加载器信息
-
获取类加载器所在jar包
-
查找资源所在jar包
-
加载指定类
8.1、 参数
| 参数名称 | 参数说明 |
|---|---|
| [l] | 按类加载实例进行统计 |
| [t] | 打印所有 ClassLoader 的继承树 |
| [a] | 列出所有 ClassLoader 加载的类,请谨慎使用 |
[c:] |
ClassLoader 的 hashcode |
[classLoaderClass:] |
指定执行表达式的 ClassLoader 的 class name |
[c: r:] |
用 ClassLoader 去查找 resource |
[c: load:] |
用 ClassLoader 去加载指定的类 |
8.2、测试
# 查看统计信息
classloader
# 按实例查看统计信息(显示hashCode)
classloader -l
# 查看继承树
classloader -t
# 查看类加载器位置
classloader -c 680f2737
# 查找资源位置
classloader -c 680f2737 -r META-INF/MANIFEST.MF
# 查找类文件位置
classloader -c 680f2737 -r java/lang/String.class
# 加载指定类
classloader -c 70dea4e --load java.lang.String
8.1.1、查看统计信息
classloader

8.1.2、按实例查看统计信息(显示hashCode)
classloader -l

8.1.3、查看继承树
classloader -t

8.1.4、查看类加载器位置
# 通过类加载器的hash,查看此类加载器实际所在的位置
classloader -c 70dea4e

8.1.5、查找资源位置
# 使用ClassLoader去查找指定资源resource所在的位置
classloader -c 70dea4e -r META-INF/MANIFEST.MF

8.1.6、查找类文件位置
# 使用ClassLoader去查找类的class文件所在的位置
classloader -c 70dea4e -r java/lang/String.class

8.1.7、加载指定类
# 使用ClassLoader去加载类
classloader -c 70dea4e --load java.lang.String

更多推荐

所有评论(0)