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

在这里插入图片描述

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐