Provides services that allow Java programming language agents to instrument programs running on the JVM. The mechanism for instrumentation is modification of the byte-codes of methods.【用于允许Java编程语言代理检测运行在JVM上的程序提供服务。检测的机制是修改方法的字节码。】

这是java.lang.instrument包的描述。使用 Instrumentation,使得开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义

如果还没入门或者想要更多知识,可以查阅

  • 官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/instrument/package-summary.html
  • 优秀博客:https://www.jianshu.com/p/b72f66da679f

我为什么会研究这个,刚开始是基于这样一个需求:有个远古项目是专门做直播APP的活动,每个活动都有对应的生命周期,加上没有做成模块化(我也在考虑怎么搞),久了之后大部分的活动都已经下线,只有极少的活动还在运营,就想着有什么办法可以检测到哪些代码是还会执行的,以便迁移。想过定时jstack或者Spring AOP,发现都不适合,幸好之前了解过这方面的知识,就觉得可以派上用场了。认真看了之后,发现平时遇到的几个痛点,也可以用Java Instrument解决(原本的需求后面再研究了...囧):

  1. 线上定位问题,想要知道某个变量执行时的值(IDEA远程Debug?):临时加日志记录变量的值
  2. 性能优化,需要知道线上执行每一段代码的耗时:临时加日志记录代码执行耗时
  3. 协助Tester去测试不可以造数据的场景(比如特定日期特定时间的逻辑):临时写死某个变量的值

以上都涉及一个相同的需求:临时修改方法体。这时候,我们就可以使用Instrumentation的redefineClasses

1、开发Agent-Class

package cn.zhh;import java.io.IOException;
import java.lang.instrument.ClassDefinition;
import java.lang.instrument.Instrumentation;
import java.nio.file.Files;
import java.nio.file.Paths;/*** Agent-Class*/
public class AgentMain {/*** 运行中代理入口** @param agentArgs 自定义参数* @param inst      增强类* @throws Exception 异常*/public static void agentmain(String agentArgs, Instrumentation inst) throws Exception {// 自定义参数英文逗号分隔:[0]-class文件绝对路径,[1]-class全名String[] args = agentArgs.split(",");// 重新定义Classinst.redefineClasses(new ClassDefinition(Class.forName(args[1]), readClassBytes(args[0])));}/*** 读取文件内容** @param classPath 文件路径* @return 文件字节数据* @throws IOException 文件读取异常*/private static byte[] readClassBytes(String classPath) throws IOException {return Files.readAllBytes(Paths.get(classPath));}
}

2、开发可执行jar包的主函数

可执行jar包可以和代理jar包分开不同项目,放在一起更加方便。需要依赖tools.jar编译(JDK提供,把{JDK根目录}/lib/tools.jar引入即可),因为使用接口编程,所以不需要区分平台的JDK。

package cn.zhh;import com.sun.tools.attach.VirtualMachine;import java.util.Arrays;
import java.util.Objects;/*** mainClass*/
public class Main {/*** 可执行jar包主函数** @param args 自定义函数* @throws Exception 异常*/public static void main(String[] args) throws Exception {if (Objects.isNull(args) || args.length != 4) {throw new RuntimeException("参数数量不正确,需要4个:第一个agent包绝对路径,第二个Java进程PID,第三个class文件绝对路径,第四个class全名");}System.out.println("Main run, args are:");Arrays.stream(args).forEach(System.out::println);VirtualMachine virtualMachine = VirtualMachine.attach(args[1]);try {virtualMachine.loadAgent(args[0], args[2] + "," + args[3]);} finally {virtualMachine.detach();}}
}

3、打包

要求:

  1. 运行时需要具体平台(Windows、Linux、Mac等)的tools.jar。所以要么把依赖放入jar包,要么执行java -jar时添加类库路径。
  2. 生成对应的MANIFEST.MF清单。

因此,推荐使用Maven

1)添加tools.jar依赖

将jar包install到本地仓库(不要使用Library依赖或者systemPath依赖):mvn install:install-file -DgroupId=com.sun -DartifactId=tools -Dversion=1.8 -Dpackaging=jar -Dfile=D:\Java\jdk1.8.0_141\lib\tools.jar

<dependency><groupId>com.sun</groupId><artifactId>tools</artifactId><version>1.8</version>
</dependency>

2)添加assembly插件,并配置清单

            <plugin><artifactId>maven-assembly-plugin</artifactId><configuration><archive><manifest><mainClass>cn.zhh.Main</mainClass></manifest><manifestEntries><Agent-Class>cn.zhh.AgentMain</Agent-Class><Can-Redefine-Classes>true</Can-Redefine-Classes></manifestEntries></archive><descriptorRefs><descriptorRef>jar-with-dependencies</descriptorRef></descriptorRefs></configuration></plugin>

3)运行assembly命令得到包含依赖的可执行jar包

4、使用

1)写一个目标程序并运行

2)使用jps命令查看Java进程pid:12780

3)修改Task类,并重新编译,将得到的字节码文件改名为Task-1.class

4)终极操作,运行jar包,见证奇迹的时候

java -jar agent-1.0-jar-with-dependencies.jar D:\IdeaProjects\java-agent\agent\target\agent-1.0-jar-with-dependencies.jar 12780 D:\IdeaProjects\java-agent\target\target\classes\cn\zhh\Task-1.class cn.zhh.Task

控制台输出

目标程序控制台输出

这不用多说了吧?鼓掌!撒花!

Java Instrument实践应用:运行中修改程序的Class相关推荐

  1. 《Java项目实践》:简单聊天程序

    <Java项目实践>:简单聊天程序 由于这个简单程序,还是涉及到很多的知识点,下面我们就一点一点的来完成. 我们熟悉的QQ聊天,就有一个界面,是吧,我们自己做一个简单的聊天程序,因此我们也 ...

  2. linux 程序占内存,linux下,一个运行中的程序,究竟占用了多少内存

    1. 在linux下,查看一个运行中的程序, 占用了多少内存, 通常的命令有php (1). ps aux:html 其中  VSZ(或VSS)列 表示,程序占用了多少虚拟内存.linux RSS列 ...

  3. Java最佳实践–多线程环境中的DateFormat

    这是有关使用Java编程语言时的拟议实践的系列文章的第一篇. 所有讨论的主题均基于用例,这些用例来自于电信行业的关键任务超高性能生产系统的开发. 在阅读本文的每个部分之前,强烈建议您参考相关的Java ...

  4. python程序内存分析_python 如何测量运行中的程序内存 -- Valgrind

    介绍 通常我们可以用python profiler去分析应用程序中哪个模块被多次调用和那个程序部分运行的速度较为缓慢,但是并不能够准确给出我们应用程序在运行中在内存中占用的大小. 比如说在金融数据中会 ...

  5. MFC中修改程序图标

    在使用MFC时,我们经常需要修改我们得到的exe文件的图标.如:写一个随机画圆的小程序,我们就希望该程序的图标是个圆或者是和圆有关的图标.所以,在这里我就记录一下我修改图标的步骤. 顺便提一下,我使用 ...

  6. Unity运行中修改物体的颜色

    Unity 中当运行到某一场景,或者触发某一事件时需要修改物体的颜色.主要是通过 MeshRenderer 来进行渲染. 1.在Unity中创建一个cube 物体,确保其上有 MeshRenderer ...

  7. WINDOWS中, 如何查看一个运行中的程序是64位还是32位的

    转自:https://blog.csdn.net/dayday3923/article/details/78597453?locationNum=7&fps=1 方法一: 任务管理器法 任务管 ...

  8. android开发字体样式,Android开发中修改程序字体的样式

    Android提供三种字体:"Sans","serif"和"monospace". 1.在Android XML文件中设置字体 可以采用an ...

  9. java int数组写入文件中_Java程序将int数组写入文件

    这是我们的文件-FileWriter writer = new FileWriter("E:/demo.txt"); 现在,考虑一个整数数组-Integer arr[] = { 1 ...

  10. 如何修改was的java路径_在eclipse中修改tomcat的部署路径操作

    在eclipse上面部署web项目后,它没有将你的项目文件放到tomcat 的目录下面.而是放在了你的工作目录下面. 你到这里去找:E:\jintao\.metadata\.plugins\org.e ...

最新文章

  1. python mysql批量insert数据、返回id_Python3 操作 MySQL 插入一条数据并返回主键 id的实例...
  2. 别再用 BeanUtils 了,这款 PO VO DTO 转换神器不香么?
  3. 开源加持 NFV借势OPNFV实现进阶之路
  4. 剑指offer——变态跳台阶
  5. TF之AutoML之AdaNet框架:AdaNet框架的简介、特点、使用方法详细攻略
  6. 再赠邓超明(帮别人名字作诗)
  7. 《Orange’s 一个操作系统的实现》1.搭建操作系统开发环境
  8. Ucloud香港1h1g云服务器低至126元一年而且可开3年限时
  9. flutter Radio 单选框
  10. java查询数据比Oracle少,java对ORACLE中的于NCHAR数据的处理,查询
  11. linux登录pg数据库密码,PostgreSQL:修改数据库用户的密码
  12. python安装计算机丢失api_Python安装后提示api-ms-win-crt-runtime-|1-1-0.dll丢失
  13. 从零实现Wod2Vec(下)
  14. Spring系列:学习Spring的资源和讨论
  15. 网络安全基础——用户与组管理
  16. android x5内核 下载地址,X浏览器-X5内核版本
  17. Uri.parse()的各种用法
  18. 移动硬盘提示数据错误循环冗余检查的文件恢复方案
  19. 别被吉利的“车企”一面晃了眼
  20. 一种获取公网ip地址并发送邮件至指定邮箱的实现方法

热门文章

  1. python不定积分教学_python 求定积分和不定积分示例
  2. Sinew探索金融衍生品领域,增强金融市场流动性
  3. linux内核vga参数,LINUX grub 修改VGA参数
  4. 域名被封跟服务器IP有没有关系?
  5. CTU CU CB PU TU
  6. 360浏览器显示服务器拒绝连接,360浏览器提示“您与此网站之间建立的连接不安全完美解决方法...
  7. 动态为Spring Boot项目中所有自定义的Controller添加过滤器的两种方法
  8. 你对自己的定位是什么,就能成为什么样的人(转)
  9. 计算机网络白龙飞,成电等你来 | 你的辅导员已上线,男神辅导员闪亮登场(一)...
  10. tkinter运行时出现无响应问题