【Arthas】Arthas retransform动态重新加载类

2023-11-18

在这里插入图片描述

1.概述

转载:Arthas retransform动态重新加载类

2.开篇

Arthas提供retransform命令来重新加载已加载的类,通过该命令可以在有限制的反编译已加载的类重新修改后生成class文件重新加载。

在测试环境中可以尝试着去使用这个命令,毕竟这个原理也是基于Instrumentation来达成的。Instrumentation提供retransformClasses来实现上述的功能。

想操作字节码,Instrumentation基本上是逃不过的基础技术

3.基础知识点

在 Java SE6 里面,最大的改变是运行时的 Instrumentation 成为可能。在 Java SE 5 中,Instrument 要求在运行前利用命令行参数或者系统参数来设置代理类在实际的运行之中,虚拟机在初始化之时(在绝大多数的 Java 类库被载入之前),instrumentation 的设置已经启动,并在虚拟机中设置了回调函数,检测特定类的加载情况,并完成实际工作

但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理,这样实际上限制了 instrument 的应用。而 Java SE 6 的新特性改变了这种情况,通过 Java Tool API 中的 attach 方式,我们可以很方便地 在运行过程中动态地设置加载代理类,以达到 instrumentation 的目的

Instrumentation提供retransformClasses重新转换提供的一组类。

该方法主要作用于已经加载过的class。可以用ClassFileTransformer对初始化过或者redifine过的class进行重新处理, 无论以前是否发生转换,此函数都将重新运行转换过程。转换后的类文件字节作为类的新定义安装。

4.Arthas的retransform

public class RetransformCommand extends AnnotatedCommand {

    private static void initTransformer() {
        if (transformer != null) {
            return;
        } else {
            synchronized (RetransformCommand.class) {
                if (transformer == null) {
                    transformer = new RetransformClassFileTransformer();
                    TransformerManager transformerManager = ArthasBootstrap.getInstance().getTransformerManager();
                    transformerManager.addRetransformer(transformer);
                }
            }
        }
    }


    @Override
    public void process(CommandProcess process) {
        // 核心操作初始化Transformer
        initTransformer();

        Map<String, byte[]> bytesMap = new HashMap<String, byte[]>();
        for (String path : paths) {
            // 加载外部的class文件的字节流
            RandomAccessFile f = null;
            try {
                f = new RandomAccessFile(path, "r");
                final byte[] bytes = new byte[(int) f.length()];
                f.readFully(bytes);
                // 通过repackage-asm的依赖能够解析字节码获取类名
                final String clazzName = readClassName(bytes);
                bytesMap.put(clazzName, bytes);
            } catch (Exception e) {
              // 省略无关代码
            } finally {
              // 省略无关代码
            }
        }

        List<RetransformEntry> retransformEntryList = new ArrayList<RetransformEntry>();
        List<Class<?>> classList = new ArrayList<Class<?>>();
        // 通过inst获取已加载的类并和指定重载的类进行比较,存在的才重新加载
        for (Class<?> clazz : inst.getAllLoadedClasses()) {

            if (bytesMap.containsKey(clazz.getName())) {

                // 省略相关代码

                ClassLoader classLoader = clazz.getClassLoader();
                if (classLoader != null && hashCode != null
                        && !Integer.toHexString(classLoader.hashCode()).equals(hashCode)) {
                    continue;
                }

                RetransformEntry retransformEntry = new RetransformEntry(clazz.getName(), bytesMap.get(clazz.getName()),
                        hashCode, classLoaderClass);
                retransformEntryList.add(retransformEntry);
                // 保存待重新加载的类
                classList.add(clazz);
                retransformModel.addRetransformClass(clazz.getName());
            }
        }

        try {
            addRetransformEntry(retransformEntryList);
            // 重新触发字节类重新加载
            inst.retransformClasses(classList.toArray(new Class[0]));
        } catch (Throwable e) {

        }
    }
}

initTransforme方法负责初始化类转换对象RetransformCommand。
整个process核心逻辑加载class文件的字节码用于重新加载,前提是重新加载的类是和inst返回的已加载的类存在交集的。

通过inst.retransformClasses操作来触发类的重新加载。

public class RetransformCommand extends AnnotatedCommand {

    static class RetransformClassFileTransformer implements ClassFileTransformer {
        @Override
        public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
                ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {

            if (className == null) {
                return null;
            }

            className = className.replace('/', '.');

            List<RetransformEntry> allRetransformEntries = allRetransformEntries();
            // 倒序,因为要执行的配置生效
            ListIterator<RetransformEntry> listIterator = allRetransformEntries
                    .listIterator(allRetransformEntries.size());
            while (listIterator.hasPrevious()) {
                RetransformEntry retransformEntry = listIterator.previous();
                int id = retransformEntry.getId();
                // 判断类名是否一致
                boolean updateFlag = false;
                // 类名一致,则看是否要比较 loader,如果不需要比较 loader,则认为成功
                if (className.equals(retransformEntry.getClassName())) {
                    if (retransformEntry.getClassLoaderClass() != null || retransformEntry.getHashCode() != null) {
                        updateFlag = isLoaderMatch(retransformEntry, loader);
                    } else {
                        updateFlag = true;
                    }
                }

                if (updateFlag) {
                    retransformEntry.incTransformCount();
                    // 返回待重新加载的字节码
                    return retransformEntry.getBytes();
                }

            }

            return null;
        }
    }
}

RetransformCommand核心在于retransformEntry.getBytes()返回待重新加载的字节码。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

【Arthas】Arthas retransform动态重新加载类 的相关文章

  • Java GuardedString - 用于加密的随机密钥是否存储在 Java 堆内存中?如果不是,那么密钥保存在哪里?

    Oracle 的 org identityconnectors common security GuardedString 要转换为 GuardedString 的原始数据需要由 EncryptorImpl class 随机生成的加密密钥
  • 将 CSV 文件读入 Java 作为数据库表

    我发现了很多关于使用 Java 读取 CSV 的帖子 并且他们所指向的 API 在读取 CSV 文件时都采用了面向行的方法 就像 当你得到一行时 获取每一列的值 我希望有一个更高级别的 API 比如在 Perl 中 DBI 允许您在 CSV
  • eclipse juno 打开时出错

    在安装 Eclipse 并正常工作一年多后 我今天打开 Eclipse Juno 并在打开工作区时收到一条错误消息 我使用的是 Windows 8 64 位 Java 64 位和 Eclipse 64 位 此后我尝试重新安装 Java 和
  • 如何在流中收集到TreeMap中?

    我有两个Collectors groupingBy在流中 我需要收集所有信息TreeMap 我的代码 Map
  • java.sql.SQLException: ORA-01005: 给定的密码为空;登录被拒绝

    我在尝试连接到数据库时遇到以下异常 java sql SQLException ORA 01005 null password given logon denied at oracle jdbc driver T4CTTIoer proce
  • 包含小时、分钟和秒的周期[关闭]

    Closed 这个问题正在寻求书籍 工具 软件库等的推荐 不满足堆栈溢出指南 help closed questions 目前不接受答案 我需要一个代表年 月 周 日 小时 分钟 秒的间隔数据类型 前三年 年 月 日 可以用Period最后
  • 从继承的受保护 Java 字段创建公共访问器

    我怎样才能完成以下工作 class Foo extends javax swing undo UndoManager increase visibility works for method override def editToBeUnd
  • 首选项活动中的广告“没有足够的空间来显示广告!需要:<480, 75>,拥有:<432, 1073741823>”

    我试图在偏好活动中展示广告 但它从未出现 Logcat 始终显示消息 没有足够的空间来显示广告 想要 有 这就是我制作广告的方式 我对广告有自定义偏好 public class AdmobPreference extends Prefere
  • 将 emoji 替换为适当的 java 代码

    我正在开发一个简单的java程序 它可以接受这样的字符串 停止 你违反了 法律 但是现在 你 并将每个表情符号替换为适当的 java 字符 我不知道该怎么称呼他们 这是一个例子 汽车表情符号 将替换为 uD83D uDE97 这允许我有一个
  • 可以混合使用 JVM 语言吗?即:Groovy 和 Clojure

    我知道你可以轻松地混合groovy java clojure java 无论什么JvmLang java 这是否也意味着我也可以让 clojure 和 groovy 代码进行交互 如果我使用 Grails 或 jRoR 我也可以在该环境中使
  • 如何连接hibernate和DB2

    我正在运行一个使用 struts 和 hibernate 的应用程序 我目前正在使用 Derby 数据库 现在我必须转向 DB2 数据库 请告诉我 我必须做什么配置 休眠配置文件 我必须设置任何类路径吗 多变的 我知道 DB2 有两个 ja
  • 为什么 Java 中的 hashCode() 可以对不同对象返回相同的值?

    引用我正在读的书中的一段话首先Java http www amazon co uk Head First Java Kathy Sierra dp 0596009208 关键是 哈希码可以相同 但不一定保证对象相等 因为使用的 哈希算法 h
  • 如何减少 JSF 中的 javax.faces.ViewState

    减少 JSF 中视图状态隐藏字段大小的最佳方法是什么 我注意到我的视图状态约为 40k 这会在每次请求和响应时下降到客户端并返回到服务器 特别是到达服务器时 这对用户来说会显着减慢 我的环境 JSF 1 2 MyFaces Tomcat T
  • Android - 保持用户登录状态

    我正在尝试使用 PHP 和 MySQLi for Android 进行登录 我不明白的是如何保持用户登录状态 我看到一个简单的教程 其中有人使用 SQLite 来保护信息 但我不知道这是否真的安全 如何保存用户信息以保持用户登录状态 谢谢
  • HashSet 与 LinkedHashSet

    它们之间有什么区别 我知道 LinkedHashSet 是 HashSet 的有序版本 维护一个跨所有元素的双向链接列表 使用此类代替 HashSet 当您关心迭代顺序时 当你迭代 HashSet 时 顺序是不可预测的 而 LinkedHa
  • 线程睡眠阻止我的 Swing 应用程序执行

    我的应用程序发生的事情是有道理的 但我不知道如何修复它 以下是我的应用程序功能的简要描述 计时器窗口应显示在屏幕右下角并显示实时时间 一小时后 它应该执行一些操作 我还没有决定该操作 我面临的问题是定时器 java当我刷新实时计时器的秒数时
  • 在 Java 服务器中验证 Windows 用户

    我正在开发一个用 Java 编写的服务器和一个在同一网络上的 Windows 计算机上运行的客户端 用 Net 编写的桌面应用程序 我希望进行一些基本身份验证 以便服务器可以确定运行客户端的用户的用户名 而不需要用户在客户端中重新输入其 W
  • Android应用程序中的模式输入

    我想知道是否有其他替代方案可以替代 Android 上平庸的 EditText 密码输入 是否有 API 或开源代码可以集成到我的应用程序中 类似于锁屏图案解锁 Intent 可能会返回哈希值 数字 字符串或代表用户输入的模式的任何内容 我
  • 对 Java 协议缓冲区对象进行一些小更改

    我想在 Java 协议缓冲区对象树的深处进行一个小更改 我可以使用 getBuilder 方法来创建一个新对象 该新对象是旧对象的克隆并进行一些更改 当深入完成此操作时 代码会变得丑陋 Quux Builder quuxBuilder fo
  • 按字母顺序对对象的 ArrayList 进行排序

    我必须创建一个方法来排序数组列表根据电子邮件按字母顺序排列对象 然后打印排序后的数组 我在排序时遇到麻烦的部分 我已经研究过并尝试使用Collections sort vehiclearray 但这对我不起作用 我是因为我需要一个叫做比较器

随机推荐

  • php简单的网站CRUD功能

    编辑器 使用的phpstrom 环境使用的 xampp 前段掌握并不熟练 引用了jQuery和bootstrap 目录 主页 数据库连接conn php home html 登录处理模块login php 登陆后可进入页面index php
  • 桥接模式下的虚拟机中ping不通网关

    桥接的虚拟机ping不通网关 配置VM 工具栏中的 编辑 gt 虚拟网络编辑器 点击添加网络 再选择桥接模式 2 打开虚拟机设置 网络适配器选择桥接模式 4 进入虚拟机 进入网络配置 5 将宿机的IP 掩码 网关等信息配置 注意 此时选择
  • angular新建组件的组成部分

    主要分为三个组成部分 一 一个ts文件 用来处理数据和功能的 初始会有一个inport 和一个 component 二 一个html文件 用来书写页面的内容 初始会有一个标签 三 一个css文件 用来书写页面样式 初始是一个空文件夹 转载于
  • redis 主从哨兵模式搭建

    一 REDIS主从模式 搭建三个节点 同一个机器 三个端口 安装包 wget http download redis io releases redis 5 0 7 tar gz 安装步骤 https blog csdn net whg18
  • 【Linux】Linux和Window下\r与\n的区别、git命令行的使用

    作者 小卢 专栏 Linux Git 喜欢的话 世间因为少年的挺身而出 而更加瑰丽 人民日报 目录 1 回车换行符在Window下和在Linux下的区别 1 1回车换行符 1 2 行缓冲区打印 1 3进度条小程序 2 git命令行的使用 2
  • JAVA SM2 数字证书生成

    文章目录 Before Start Build with Maven QuickStart 生成自签名公私钥对 证书签名算法算法提供者 设置证书信息 标识信息构造 DN 获取扩展密钥用途构造 可选 证书信息构造 X 509格式证书对象生成
  • C语言版通讯录——动态存储(进阶版)

    前言 之前 我出了一期关于通讯录管理系统的项目实现 里面可以实现通讯录人员的增添改查显示以及排序统计功能 但那种只是以静态开辟内存的方式进行编写 大小固定死 若是达到上限便不可以再进行添加 这次优化 我会将静态存储改为动态存储 达到可以永久
  • idea双击启动无效,idea卡顿问题

    idea双击启动无效 大概率是关机时没有正确关闭idea 再次开机导致无法正常启动idea 1 通过任务管理器杀死idea进程后重启idea 2 需要修改配置 打开 以各自电脑实际为准 C Program Files JetBrains I
  • OpenResty简介、编译安装、新增 sticky模块(再编译)、升级

    OpenResty 也被称为 ngx openresty 是一个基于 Nginx 与 Lua 的高性能 Web 平台 其内部集成了大量精良的 Lua 库 第三方的Nginx模块和大部分系统依赖包 用于方便地搭建能够处理超高并发 扩展性极高的
  • 找不到boost/noncopyable.hpp问题

    本人在vs2013使用boost库的时候 提示找不到boost noncopyable hpp文件 在百度和贴吧上找了很多资料 安装没出问题 只是在包含include的路径是为D boost 1 56 0 boost 本人将boost库直接
  • c++ 友元

    c 类中可以用friend关键字来声明友元 友元可以是另一个类或者类外的其他函数 友元只具有单向性例如 在类A中将类B声明为友元B可以查看A中的所有成员 而A不能查看B的私有成员 同理将一个函数声明为友元时这个函数也可以访问类中的所有成员
  • 6、微信小程序的布局

    文章目录 前言 一 传统基本布局 1 盒子模型 2 显示方式Display 3 定位position 4 溢出属性Overflow 5 浮动Float 6 对齐 二 Flex弹性布局 1 基本属性 2 使用示例 前言 wxss 指的是 We
  • (转)42个超实用的Google Chrome 插件,值得你拥有,建议收藏!

    每日英文 When friends ignore you don t be sad everyone has his own life who can not always be with you 当朋友忽略你时 不要伤心 每个人都有自己的
  • MySql语句查询某一级节点的所有子节点

    MySql语句查询某一级节点的所有子节点 在日常项目中 我们总能用到树型结构的数据 我们用代码去进行查询是比较麻烦的 这里提供一种sql语句查询父节点和子节点的方法 说明 只能当前节点查出所有子节点 不包含与当前节点平级的节点 且子节点是全
  • 事务,不只ACID

    1 什么是事务 应用在运行时可能会发生数据库 硬件的故障 应用与数据库的网络连接断开或多个客户端端并发修改数据导致预期之外的数据覆盖问题 为了提高应用的可靠性和数据的一致性 事务应运而生 从概念上讲 事务是应用程序将多个读写操作组合成一个逻
  • 微信h5分享好友和朋友圈功能

    在开发公众号H5项目时 如果想和小程序一样有分享朋友圈和好友功能时发现会不一样 开发微信小程序时做分享有会有onShareAppMessage 这个方法 因为H5有许多限制 所以在做微信H5分享时就比较麻烦了 首先明确一点 微信H5分享是没
  • Disruptor 详解

    Disruptor 详解 想了解一个项目 最好的办法就是 把它的源码搞到本地自己捣鼓 在网上看了 N 多人对 Disruptor 速度的吹捧 M 多人对它的机制分析 就连 Disruptor 官方文档中 也 NB 哄哄自诩 At LMAX
  • Netty学习17-Netty的可视化

    学习第41节 Netty 可视化方案 Console 日志定时输出 JMX 实时展示 ELKK TIG etc ChannelHandler Sharable public class MetricsHandler extends Chan
  • 08-----查找某个文件的路径

    1 使用find命令查找 关于find这个命令 非常强大 它可以根据文件大小 属性 用户 文件名等去查找匹配的文件 这里不再解释 这里只是列出常用的方法 查找某个文件并不输出错误 find name GetInfo 2 gt dev nul
  • 【Arthas】Arthas retransform动态重新加载类

    1 概述 转载 Arthas retransform动态重新加载类 2 开篇 Arthas提供retransform命令来重新加载已加载的类 通过该命令可以在有限制的反编译已加载的类重新修改后生成class文件重新加载 在测试环境中可以尝试