近来需要完成一个feature:修改已load到JVM中的某个class,对其加一些代码,以此来动态修改运行中的程序。
对着这个feature我找到的方案是agent+Instrumentation+ASM
一路做下来有以下几点比较有意思:
1)动态attach agent到某个JVM进程
一般使用agent都是静态的,直接在运行某程序时加agent参数,这样agent会先于程序启动,这个不符合我的需求,我找到一个动态attach agent的方法,具体细节见以下代码:
- public static void attach(String pid) throws Exception {
- try {
- String agentPath = "/cutemock-agent.jar";
- String tmp = Main.class.getClassLoader().getResource("com/taobao/lp/cutemock/agent/Main.class").toString();
- tmp = tmp.substring(0, tmp.indexOf("!"));
- tmp = tmp.substring("jar:".length(), tmp.lastIndexOf("/"));
- agentPath = tmp + agentPath;
- agentPath = new File(new URI(agentPath)).getAbsolutePath();
- VirtualMachine vm = null;
- if (debug) {
- debugPrint("attaching to " + pid);
- }
- vm = VirtualMachine.attach(pid);
- if (debug) {
- debugPrint("attached to " + pid);
- }
- if (debug) {
- debugPrint("loading " + agentPath);
- }
- String agentArgs = "port=" + port;
- if (debug) {
- agentArgs += ",debug=true";
- }
- if (debug) {
- debugPrint("agent args: " + agentArgs);
- }
- vm.loadAgent(agentPath, agentArgs);
- if (debug) {
- debugPrint("loaded " + agentPath);
- }
- } catch (RuntimeException re) {
- throw re;
- } catch (IOException ioexp) {
- throw ioexp;
- } catch (Exception exp) {
- throw exp;
- }
- }
这段代码的关键是要找到agent的jar包,然后通过VirtualMachine.attach和VirtualMachine.loadAgent把agent attach到pid上
2)通过Instrumentation修改已load了的class
见如下代码:
- Class[] classes = inst.getAllLoadedClasses();
- for(Class clazz : classes){
- if(clazz.getName().equals(CLASS_NAME)){
- System.out.println("add transformer to TBRemotingRPCProtocolComponent.class");
- inst.addTransformer(new MyClassFileTransformer(),true);
- inst.retransformClasses(clazz);
- }
- }
关键在于inst.addTransformer(new MyClassFileTransformer(),true);这个true参数,inst.retransformClasses(clazz);只 会重新修改addTransformer中canRetransform==true的
3)通过asm eclipse plugin方便修改class
大家都知道可以通过asm来修改class,但其api及其难用,比如我仅仅只想加一行:
targetURL = MockUtil.getTargetUrl(metadata.getUniqueName(), request.getMethodName(), targetURL);
翻译为asm:
- mv.visitVarInsn(Opcodes.ALOAD, 2);
- mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/taobao/hsf/model/metadata/ServiceMetadata", "getUniqueName", "()Ljava/lang/String;");
- mv.visitVarInsn(Opcodes.ALOAD, 1);
- mv.visitMethodInsn(Opcodes.INVOKEVIRTUAL, "com/taobao/hsf/domain/HSFRequest", "getMethodName", "()Ljava/lang/String;");
- mv.visitVarInsn(Opcodes.ALOAD, 3);
- mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/taobao/lp/cutemock/agent/MockUtil", "getTargetUrl", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;");
- mv.visitVarInsn(Opcodes.ASTORE, 3);
- Label l4 = new Label();
- mv.visitLabel(l4);
但asm提供了一个eclipse plugin,更新地址为:http://andrei.gmxhome.de/eclipse/
它可以对比出修改前后的class的差异,并自动翻译为asm代码
以上是我这两天玩动态修改class的一些心得,有点乱,但确实是不断尝试后的心得
相关推荐
本文将详细讲解如何使用JVM动态执行Groovy脚本的方法,主要包括利用JShell执行代码、调试模式下动态执行代码以及利用javax.script包执行Groovy脚本。以下是对各知识点的详细说明。 1. 利用JShell执行代码 Java 9 ...
在JVM执行子系统中,类加载器负责将Class文件加载到JVM内存中。加载后,类文件会经过验证、准备、解析等步骤,最终进行初始化,使得类中的静态变量得到正确的初始化值。在加载过程中,JVM会使用类加载器创建一个类的...
这整个过程,我们编写的Java程序没有做任何改变,仅仅是通过JVM这一“中间层”,就能在不同平台上运行,真正实现了“一次编译,到处运行”的目的。 什么是JVM JVM,即Java Virtual Machine,Java虚拟机。它通过...
3. **动态性**:JVM允许程序在运行时动态加载类和资源,增强了软件的灵活性和可扩展性。 4. **内存管理**:自动的垃圾回收机制简化了内存管理,减少了程序员的工作负担。 5. **性能优化**:JIT编译器可以根据运行...
在执行Java程序时,JVM会将字节码(.class文件)转换为机器码并逐条执行。 在这个简单的JVM实现中,`JVM.java`文件很可能是模拟了JVM的执行引擎部分。执行引擎负责解析和执行字节码,其核心操作就是堆栈操作。在...
**用途**:显示或更改虚拟机的动态属性和系统属性。 **命令格式**: ``` jinfo [option] pid ``` **执行示例**: ``` jinfo -flag UseConcMarkSweepGC 3700 ``` **功能**:查询或修改虚拟机的运行时配置。 #####...
#### 一、JVM加载Class文件的原理机制 在Java开发过程中,深入理解JVM如何加载Class文件至关重要,尤其是在面试时,这一知识点往往是考察的重点之一。下面将详细阐述JVM加载Class文件的基本原理及其背后的机制。 *...
这有助于避免频繁的堆内存动态调整,提升性能。 11. **-Xmx**: 设置最大Java堆大小。限制应用程序的最大内存使用量,防止过度占用系统资源。 12. **-Xss**: 设置Java线程栈大小。合理的设置可以避免线程栈溢出,...
6. **运行时保护**:ClassFinal可能在运行时进行动态检查,确保代码在预期环境中运行,防止被注入恶意代码或在不受信任的环境下执行。 在实际使用中,开发者需要按照ClassFinal的文档指导,将源代码编译成class文件...
首先,JVM由Java API和JRE组成,其中JVM的主要职责是通过类加载器(Class Loader)加载Java程序,并根据Java API来执行这些程序。JVM的设计是基于栈的,与常见的基于寄存器的计算机架构(如Intel x86和ARM)不同。...
自Java 9引入模块系统后,`.class`文件的查找和加载方式有所改变。传统的ClassPath配置方式可能会被模块路径(Module Path)所替代,这有助于管理和隔离依赖。 10. **注解处理**: Java允许在`.java`源文件中使用...
### JVM性能调优——JVM内存管理与GC回收详解 #### 概览 在现代软件开发领域,Java凭借其强大的跨平台能力和丰富的生态系统成为企业级应用的首选语言之一。然而,随着应用程序复杂度的提高以及业务需求的变化,...
2. **内部表示(Internal Representation)**:Java源代码被编译成字节码(`.class`文件),其中字符串常量以UTF-8格式存储。在运行时,这些字符串在JVM内部以`char`数组的形式存在,即使用Unicode编码。 3. **字节...
在深入探讨JVM机制之前,我们首先需要理解Java虚拟机(JVM)是Java程序运行的基础,它负责解析和执行字节码,提供了一个与硬件无关的运行环境。本手册将详细阐述JVM的各个方面,帮助你更好地理解和优化Java应用程序...
JVM包含两个子系统和两个组件,两个子系统为Class loader(类装载)、Execution engine(执行引擎);两个组件为Runtime data area(运行时数据区)、Native Interface(本地接口)。 * Class loader(类装载):...
- **功能**:`jinfo` 允许用户查询和更改运行中的Java应用的系统属性。 - **常用操作**: - 查询属性:`jinfo -flag flagname pid`。 - 设置属性:`jinfo -flag flagname value pid`。 ##### 4. jhat:虚拟机堆...
当我们运行一个JAR文件时,Java虚拟机(JVM)会加载其中的class文件并执行其中的代码。 修改JAR文件中的class文件涉及以下几个步骤: 1. **解压JAR文件**:使用解压缩工具(如WinRAR、7-Zip或命令行的jar命令)将...
通过修改CLASS文件,开发者可以调整程序的行为,比如改变方法的实现、修复bug、增强性能,甚至添加新的功能,而无需重新编译整个项目。 在压缩包中的“JBE反编译器”可能是指Java Bytecode Editor(JBE),这是一个...
一旦被声明为`final`,变量的值就不能改变,方法不能被重写,类不能被继承。 2. `finally`:`finally`块在异常处理中用于确保一段代码无论是否发生异常都会被执行。它通常包含清理资源(如关闭文件流)的操作。 3. `...
从性能图表中可以看出,当程序行为改变时,执行时间出现了显著的增加,这表明JVM进行了去优化操作。 #### 调用计数器 调用计数器是JVM用来识别热点代码的关键机制之一。每当一个方法被调用时,其调用计数器就会...