Arthas这类操作具体是如何实现的?

2026-05-19 17:211阅读0评论SEO问题
  • 内容介绍
  • 文章标签
  • 相关推荐

本文共计2008个文字,预计阅读时间需要9分钟。

Arthas这类操作具体是如何实现的?

Arthas 一站式操作,查找、编译、更新 Arthas 类操作:

1. classLoader 查询 JVM 中存在的 classloader classloader name numberOfInstances loadedCountTotal BootstrapClassLoader 1 2340 com.taobao.arthas.agent.ArthasClassloader

arthas一站式类操作,查找,编译,更新 Arthas之类操作 1. classLoader

查询当前JVM中存在的classloader

classloader

name numberOfInstances loadedCountTotal BootstrapClassLoader 1 2340 com.taobao.arthas.agent.ArthasClassloader 1 1345 sun.misc.Launcher$AppClassLoader 1 145 sun.misc.Launcher$ExtClassLoader 1 52 sun.reflect.DelegatingClassLoader 17 17 com.system.framework.CoutomerClassLoader 2 2 // author:herbert qq:464884492 date:20220330

查询当前JVM中存在的classloader,注意我们自定义实现的 com.system.framework.CoutomerClassLoader

classloader -l

name loadedCount hash parent BootstrapClassLoader 2340 null null com.system.framework.CoutomerClassLoader@6a6824be 1 6a6824be sun.misc.Launcher$AppClassLoader@7cd84586 com.system.framework.CoutomerClassLoader@4aa8f0b4 1 4aa8f0b4 sun.misc.Launcher$AppClassLoader@7cd84586 com.taobao.arthas.agent.ArthasClassloader@5cd3ae63 1345 5cd3ae63 sun.misc.Launcher$ExtClassLoader@7e6cbb7a sun.misc.Launcher$AppClassLoader@7cd84586 145 7cd84586 sun.misc.Launcher$ExtClassLoader@7e6cbb7a sun.misc.Launcher$ExtClassLoader@7e6cbb7a 52 7e6cbb7a null // author:herbert qq:464884492 date:20220330

查询当前JVM中classloder之间继承关系,注意我们自定义实现的 com.system.framework.CoutomerClassLoader

Arthas这类操作具体是如何实现的?

classloader -t

+-BootstrapClassLoader +-sun.misc.Launcher$ExtClassLoader@7e6cbb7a +-com.taobao.arthas.agent.ArthasClassloader@5cd3ae63 +-sun.misc.Launcher$AppClassLoader@7cd84586 +-com.system.framework.CoutomerClassLoader@6a6824be +-com.system.framework.CoutomerClassLoader@4aa8f0b4

查询指定的classLoader加载了哪些资源

classloader -c 7e6cbb7a classloader --classLoaderClass sun.misc.Launcher$ExtClassLoade

file:/C:/Program%20Files/Java/jre1.8.0_221/lib/ext/access-bridge-64.jar file:/C:/Program%20Files/Java/jre1.8.0_221/lib/ext/cldrdata.jar ...

查询具体资源路径

classloader -c 7cd84586 -r com/system/framework/FrameworkApplicationTests.class

file:/D:/projects/javaprj/framework/target/test-classes/com/system/framework/FrameworkApplicationTests.class

查询指定classloader已经加载的类

classloader -c 4aa8f0b4 -a

hash:1252585652, com.system.framework.CoutomerClassLoader@4aa8f0b4 com.system.framework.EncryptClass 2. Class从哪里加载

通过命令sc查找,重点关注 code-source class-loade classLoaderHash

查找当前环境中加载类中包含EncryptClass的类,code-source为空,表示该class是运行时从代码加载,没有具体路径

sc -d *EncryptClass*

class-info com.system.framework.EncryptClass code-source name com.system.framework.EncryptClass isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name EncryptClass modifier public annotation interfaces super-class +-java.lang.Object class-loader +-com.system.framework.CoutomerClassLoader@6a6824be +-sun.misc.Launcher$AppClassLoader@7cd84586 +-sun.misc.Launcher$ExtClassLoader@7e6cbb7a classLoaderHash 6a6824be // author:herbert qq:464884492 date:20220330

code-source 显示具体class加载来源,如果是从jar包中加载会显示具体jar路径

sc -d *CoutomerClassLoader*

class-info com.system.framework.CoutomerClassLoader code-source /D:/projects/javaprj/framework/target/test-classes/ name com.system.framework.CoutomerClassLoader isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name CoutomerClassLoader modifier public annotation interfaces super-class +-java.lang.ClassLoader +-java.lang.Object class-loader +-sun.misc.Launcher$AppClassLoader@7cd84586 +-sun.misc.Launcher$ExtClassLoader@7e6cbb7a classLoaderHash 7cd84586 // author:herbert qq:464884492 date:20220330 3. 同名不同版本jar生效版本

根据jar存放位置,推算出对应的classloader,比如查找当前环境加载哪个版本的log4j

classloader -classLoaderClass sun.misc.Launcher$AppClassLoader | grep log4j

file:/D:/maven/org/springframework/boot/spring-boot-starter-log4j2/2.0.5.RELEASE/spring-boot-starter-log4j2-2.0.5.RELEASE.jar file:/D:/maven/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar file:/D:/maven/org/apache/logging/log4j/log4j-jul/2.10.0/log4j-jul-2.10.0.jar file:/D:/maven/org/apache/logging/log4j/log4j-api/2.16.0/log4j-api-2.16.0.jar file:/D:/maven/org/apache/logging/log4j/log4j-core/2.16.0/log4j-core-2.16.0.jar

从上可知道,当前环境使用的是log4j-api-2.10.0.jar

根据jar中包含的类名,使用sc命令查找

sc -d *LogManager

class-info org.apache.logging.log4j.LogManager code-source /D:/maven/org/apache/logging/log4j/log4j-api/2.16.0/log4j-api-2.16.0.jar name org.apache.logging.log4j.LogManager isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name LogManager modifier public annotation interfaces super-class +-java.lang.Object class-loader +-sun.misc.Launcher$AppClassLoader@7cd84586 +-sun.misc.Launcher$ExtClassLoader@7e6cbb7a classLoaderHash 7cd84586 // author:herbert qq:464884492 date:20220330

从 code-source 中可以知道当前加载的文件具体路径

针对日志框架可以通过logger命名查看codeSource来了解jar来源

logger

name root class org.apache.logging.log4j.core.config.LoggerConfig classLoader sun.misc.Launcher$AppClassLoader@7cd84586 classLoaderHash 7cd84586 level INFO config XmlConfiguration[location=D:\projects\javaprj\framework\target\classes\log4j2.xml] additivity true codeSource file:/D:/maven/org/apache/logging/log4j/log4j-core/2.16.0/log4j-core-2.16.0.jar appenders name Console class org.apache.logging.log4j.core.appender.ConsoleAppender classLoader sun.misc.Launcher$AppClassLoader@7cd84586 classLoaderHash 7cd84586 target SYSTEM_OUT // author:herbert qq:464884492 date:20220330 4. 反编译得到源码

从上边classloader列表可以知道,当前JVM中实例化了两个自定义的类加载器CoutomerClassLoader。他们分别加载类 com.system.framework.EncryptClass。当前这个class已经加密,在我们源代码环境是没有这个类源文件。我们自定义加载器,加载代码如下

@Override public Class<?> loadClass(String name) throws ClassNotFoundException { byte[] classByte = Base64Utils.decodeFromString(ArtahsDemoClassLoader.encrypt); if ("com.system.framework.EncryptClass".equals(name)) { return defineClass(name, classByte, 0, classByte.length); } return super.loadClass(name); }

控制台显示源代码

jad -c 3e2e18f2 --source-only com.system.framework.EncryptClass --lineNumber false

控制台不显示,直接输出文件

jad -c 3e2e18f2 --source-only com.system.framework.EncryptClass --lineNumber false > D:\\EncryptClass.java

得到反编译代码如下

// author:herbert qq:464884492 date:20220330 /* * Decompiled with CFR. */ package com.system.framework; public class EncryptClass { String note; public EncryptClass() { } public EncryptClass(String note) { this.note = note; } public void print() { System.out.println("源文件初始输出==>" + this.note); } public void setNote(String note) { this.note = note; } public String getNote() { return this.note; } }

从代码可知,当前控制台会打印对应的信息为源文件初始输出==>note。主程序代码每5秒输出一次,输出信息如下

... ======第190次输出====== 源文件初始输出==>testRefect--1 源文件初始输出==>testRefect--2 ======第191次输出====== 源文件初始输出==>testRefect--1 源文件初始输出==>testRefect--2 ... 5. 内存编译得到字节码

把反编译文件中的 print方法做如下改动

public void print() { System.out.println("反编译后重新加载==>" + this.note); }

执行内存编译编译命令,这里需要注意下,当前运行环境一定是jdk环境而不是jre环境

mc -c 3e2e18f2 -d D:\\ D:\\EncryptClass.java

Memory compiler output: D:\com\system\framework\EncryptClass.class Affect(row-cnt:1) cost in 738 ms.

出现如上信息,表示编译成功

6. 加载改动后的Class到JVM

得到我们修改后的class,需要重新装载到JVM以替换之前的class字节码

retransform -c 3e2e18f2 D:\\com\\system\\framework\\EncryptClass.class

成功以后,回到控制台。可以看到输出信息已经改变

======第73次输出====== 源文件初始输出==>testRefect--1 源文件初始输出==>testRefect--2 ======第74次输出====== 反编译后重新加载==>testRefect--1 反编译后重新加载==>testRefect--2

从结果来看,最初猜测,指定classloder,只会影响当前classloader加载的类。可是另一个classloader加载类输出也改变了。说明同一个类的字节码在jvm一定只存在一份

查看已经重新加载过的类

retransform -l

Id ClassName TransformCount LoaderHash LoaderClassName 1 com.system.framework.EncryptClass 1 3e2e18f2 null

还原重新加载前的字节信息,一定依次执行如下两条命令。classPattern 支持通配符

retransform --deleteAll retransform --classPattern com.system.framework.EncryptClass

======第260次输出====== 反编译后重新加载==>testRefect--1 反编译后重新加载==>testRefect--2 ======第261次输出====== 源文件初始输出==>testRefect--1 源文件初始输出==>testRefect--2 7. 总结

欢迎感兴趣的朋友关注我的订阅号“小院不小”,或点击下方二维码关注。我将多年开发中遇到的难点,以及一些有意思的功能,体会都会一一发布到我的订阅号中

转载请注明来源
作者:杨瀚博
QQ:464884492

本文共计2008个文字,预计阅读时间需要9分钟。

Arthas这类操作具体是如何实现的?

Arthas 一站式操作,查找、编译、更新 Arthas 类操作:

1. classLoader 查询 JVM 中存在的 classloader classloader name numberOfInstances loadedCountTotal BootstrapClassLoader 1 2340 com.taobao.arthas.agent.ArthasClassloader

arthas一站式类操作,查找,编译,更新 Arthas之类操作 1. classLoader

查询当前JVM中存在的classloader

classloader

name numberOfInstances loadedCountTotal BootstrapClassLoader 1 2340 com.taobao.arthas.agent.ArthasClassloader 1 1345 sun.misc.Launcher$AppClassLoader 1 145 sun.misc.Launcher$ExtClassLoader 1 52 sun.reflect.DelegatingClassLoader 17 17 com.system.framework.CoutomerClassLoader 2 2 // author:herbert qq:464884492 date:20220330

查询当前JVM中存在的classloader,注意我们自定义实现的 com.system.framework.CoutomerClassLoader

classloader -l

name loadedCount hash parent BootstrapClassLoader 2340 null null com.system.framework.CoutomerClassLoader@6a6824be 1 6a6824be sun.misc.Launcher$AppClassLoader@7cd84586 com.system.framework.CoutomerClassLoader@4aa8f0b4 1 4aa8f0b4 sun.misc.Launcher$AppClassLoader@7cd84586 com.taobao.arthas.agent.ArthasClassloader@5cd3ae63 1345 5cd3ae63 sun.misc.Launcher$ExtClassLoader@7e6cbb7a sun.misc.Launcher$AppClassLoader@7cd84586 145 7cd84586 sun.misc.Launcher$ExtClassLoader@7e6cbb7a sun.misc.Launcher$ExtClassLoader@7e6cbb7a 52 7e6cbb7a null // author:herbert qq:464884492 date:20220330

查询当前JVM中classloder之间继承关系,注意我们自定义实现的 com.system.framework.CoutomerClassLoader

Arthas这类操作具体是如何实现的?

classloader -t

+-BootstrapClassLoader +-sun.misc.Launcher$ExtClassLoader@7e6cbb7a +-com.taobao.arthas.agent.ArthasClassloader@5cd3ae63 +-sun.misc.Launcher$AppClassLoader@7cd84586 +-com.system.framework.CoutomerClassLoader@6a6824be +-com.system.framework.CoutomerClassLoader@4aa8f0b4

查询指定的classLoader加载了哪些资源

classloader -c 7e6cbb7a classloader --classLoaderClass sun.misc.Launcher$ExtClassLoade

file:/C:/Program%20Files/Java/jre1.8.0_221/lib/ext/access-bridge-64.jar file:/C:/Program%20Files/Java/jre1.8.0_221/lib/ext/cldrdata.jar ...

查询具体资源路径

classloader -c 7cd84586 -r com/system/framework/FrameworkApplicationTests.class

file:/D:/projects/javaprj/framework/target/test-classes/com/system/framework/FrameworkApplicationTests.class

查询指定classloader已经加载的类

classloader -c 4aa8f0b4 -a

hash:1252585652, com.system.framework.CoutomerClassLoader@4aa8f0b4 com.system.framework.EncryptClass 2. Class从哪里加载

通过命令sc查找,重点关注 code-source class-loade classLoaderHash

查找当前环境中加载类中包含EncryptClass的类,code-source为空,表示该class是运行时从代码加载,没有具体路径

sc -d *EncryptClass*

class-info com.system.framework.EncryptClass code-source name com.system.framework.EncryptClass isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name EncryptClass modifier public annotation interfaces super-class +-java.lang.Object class-loader +-com.system.framework.CoutomerClassLoader@6a6824be +-sun.misc.Launcher$AppClassLoader@7cd84586 +-sun.misc.Launcher$ExtClassLoader@7e6cbb7a classLoaderHash 6a6824be // author:herbert qq:464884492 date:20220330

code-source 显示具体class加载来源,如果是从jar包中加载会显示具体jar路径

sc -d *CoutomerClassLoader*

class-info com.system.framework.CoutomerClassLoader code-source /D:/projects/javaprj/framework/target/test-classes/ name com.system.framework.CoutomerClassLoader isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name CoutomerClassLoader modifier public annotation interfaces super-class +-java.lang.ClassLoader +-java.lang.Object class-loader +-sun.misc.Launcher$AppClassLoader@7cd84586 +-sun.misc.Launcher$ExtClassLoader@7e6cbb7a classLoaderHash 7cd84586 // author:herbert qq:464884492 date:20220330 3. 同名不同版本jar生效版本

根据jar存放位置,推算出对应的classloader,比如查找当前环境加载哪个版本的log4j

classloader -classLoaderClass sun.misc.Launcher$AppClassLoader | grep log4j

file:/D:/maven/org/springframework/boot/spring-boot-starter-log4j2/2.0.5.RELEASE/spring-boot-starter-log4j2-2.0.5.RELEASE.jar file:/D:/maven/org/apache/logging/log4j/log4j-slf4j-impl/2.10.0/log4j-slf4j-impl-2.10.0.jar file:/D:/maven/org/apache/logging/log4j/log4j-jul/2.10.0/log4j-jul-2.10.0.jar file:/D:/maven/org/apache/logging/log4j/log4j-api/2.16.0/log4j-api-2.16.0.jar file:/D:/maven/org/apache/logging/log4j/log4j-core/2.16.0/log4j-core-2.16.0.jar

从上可知道,当前环境使用的是log4j-api-2.10.0.jar

根据jar中包含的类名,使用sc命令查找

sc -d *LogManager

class-info org.apache.logging.log4j.LogManager code-source /D:/maven/org/apache/logging/log4j/log4j-api/2.16.0/log4j-api-2.16.0.jar name org.apache.logging.log4j.LogManager isInterface false isAnnotation false isEnum false isAnonymousClass false isArray false isLocalClass false isMemberClass false isPrimitive false isSynthetic false simple-name LogManager modifier public annotation interfaces super-class +-java.lang.Object class-loader +-sun.misc.Launcher$AppClassLoader@7cd84586 +-sun.misc.Launcher$ExtClassLoader@7e6cbb7a classLoaderHash 7cd84586 // author:herbert qq:464884492 date:20220330

从 code-source 中可以知道当前加载的文件具体路径

针对日志框架可以通过logger命名查看codeSource来了解jar来源

logger

name root class org.apache.logging.log4j.core.config.LoggerConfig classLoader sun.misc.Launcher$AppClassLoader@7cd84586 classLoaderHash 7cd84586 level INFO config XmlConfiguration[location=D:\projects\javaprj\framework\target\classes\log4j2.xml] additivity true codeSource file:/D:/maven/org/apache/logging/log4j/log4j-core/2.16.0/log4j-core-2.16.0.jar appenders name Console class org.apache.logging.log4j.core.appender.ConsoleAppender classLoader sun.misc.Launcher$AppClassLoader@7cd84586 classLoaderHash 7cd84586 target SYSTEM_OUT // author:herbert qq:464884492 date:20220330 4. 反编译得到源码

从上边classloader列表可以知道,当前JVM中实例化了两个自定义的类加载器CoutomerClassLoader。他们分别加载类 com.system.framework.EncryptClass。当前这个class已经加密,在我们源代码环境是没有这个类源文件。我们自定义加载器,加载代码如下

@Override public Class<?> loadClass(String name) throws ClassNotFoundException { byte[] classByte = Base64Utils.decodeFromString(ArtahsDemoClassLoader.encrypt); if ("com.system.framework.EncryptClass".equals(name)) { return defineClass(name, classByte, 0, classByte.length); } return super.loadClass(name); }

控制台显示源代码

jad -c 3e2e18f2 --source-only com.system.framework.EncryptClass --lineNumber false

控制台不显示,直接输出文件

jad -c 3e2e18f2 --source-only com.system.framework.EncryptClass --lineNumber false > D:\\EncryptClass.java

得到反编译代码如下

// author:herbert qq:464884492 date:20220330 /* * Decompiled with CFR. */ package com.system.framework; public class EncryptClass { String note; public EncryptClass() { } public EncryptClass(String note) { this.note = note; } public void print() { System.out.println("源文件初始输出==>" + this.note); } public void setNote(String note) { this.note = note; } public String getNote() { return this.note; } }

从代码可知,当前控制台会打印对应的信息为源文件初始输出==>note。主程序代码每5秒输出一次,输出信息如下

... ======第190次输出====== 源文件初始输出==>testRefect--1 源文件初始输出==>testRefect--2 ======第191次输出====== 源文件初始输出==>testRefect--1 源文件初始输出==>testRefect--2 ... 5. 内存编译得到字节码

把反编译文件中的 print方法做如下改动

public void print() { System.out.println("反编译后重新加载==>" + this.note); }

执行内存编译编译命令,这里需要注意下,当前运行环境一定是jdk环境而不是jre环境

mc -c 3e2e18f2 -d D:\\ D:\\EncryptClass.java

Memory compiler output: D:\com\system\framework\EncryptClass.class Affect(row-cnt:1) cost in 738 ms.

出现如上信息,表示编译成功

6. 加载改动后的Class到JVM

得到我们修改后的class,需要重新装载到JVM以替换之前的class字节码

retransform -c 3e2e18f2 D:\\com\\system\\framework\\EncryptClass.class

成功以后,回到控制台。可以看到输出信息已经改变

======第73次输出====== 源文件初始输出==>testRefect--1 源文件初始输出==>testRefect--2 ======第74次输出====== 反编译后重新加载==>testRefect--1 反编译后重新加载==>testRefect--2

从结果来看,最初猜测,指定classloder,只会影响当前classloader加载的类。可是另一个classloader加载类输出也改变了。说明同一个类的字节码在jvm一定只存在一份

查看已经重新加载过的类

retransform -l

Id ClassName TransformCount LoaderHash LoaderClassName 1 com.system.framework.EncryptClass 1 3e2e18f2 null

还原重新加载前的字节信息,一定依次执行如下两条命令。classPattern 支持通配符

retransform --deleteAll retransform --classPattern com.system.framework.EncryptClass

======第260次输出====== 反编译后重新加载==>testRefect--1 反编译后重新加载==>testRefect--2 ======第261次输出====== 源文件初始输出==>testRefect--1 源文件初始输出==>testRefect--2 7. 总结

欢迎感兴趣的朋友关注我的订阅号“小院不小”,或点击下方二维码关注。我将多年开发中遇到的难点,以及一些有意思的功能,体会都会一一发布到我的订阅号中

转载请注明来源
作者:杨瀚博
QQ:464884492