• 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

    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. 总结

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

  • 相关阅读:
    3185 队列练习 1 3186 队列练习 2
    1063 合并果子
    堆排序
    奇怪的电梯
    3411 洪水
    2010 求后序遍历
    1729 单词查找树
    3137 栈练习1
    2821 天使之城
    括弧匹配检验(check.cpp)
  • 原文地址:https://www.cnblogs.com/yfrs/p/arthas_class.html
Copyright © 2020-2023  润新知