java.lang.instrument是java 5开始引入的,它把 Java 的 instrument 功能从本地代码中解放出来,使之可以用 Java 代码的方式解决问题。使用 Instrumentation,开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义.
Java5的特性:运行前利用命令行参数或者系统参数来设置代理类,虚拟机在初始化之时(在绝大多数的 Java 类库被载入之前),instrumentation 的设置已经启动,并在虚拟机中设置了回调函数,检测特定类的加载情况,并根据设置完全特定功能.
例子1:
package instrumentexample; public class MainClass { public static void main(String[] args) throws InterruptedException { System.out.println("MainClass invoke main function"); } }
package instrumentexample; import java.io.IOException; import java.lang.instrument.Instrumentation; public class Premain { public static void premain(String agentArgs, Instrumentation inst) throws IOException { Class[] classes = inst.getAllLoadedClasses(); for (Class classstr : classes) { System.out.println("Class:" + classstr.getName() + ";ClassLoader:" + classstr.getClassLoader()); } } }
javac instrumentexample/Premain.java
jar -cvf agent.jar instrumentexample/Premain.class
打包出一个agent.jar,并修改MANIFEST.MF 文件,添加 Premain-Class属性
Manifest-Version: 1.0 Premain-Class: instrumentexample.Premain Created-By: 1.6.0_20 (Sun Microsystems Inc.)
执行:java -javaagent:agent.jar instrumentexample/MainClass
打印结果可看到:Class:instrumentexample.Premain;ClassLoader:sun.misc.Launcher$AppClassLoader@df6ccd
却看不到对应的MainClass被classload加载. 这个说明了在Premain被加载并执行后,MainClass还未被AppClassLoader加载入JVM. 而对应其他的被BootstrapClassLoader加载的类都已经被完全被加载了.莫忘记了,连同AppClassLoader这个类是Java实现,也是需要被ClassLoader加载的,而这个加载器正是BootstrapClassLoader.
instrument如何实现改变方法的执行呢?通过字节码修改方式.
例子2:
package instrumentexample; public class Person { public void sayName() { System.out.println("I am Justin"); } } package instrumentexample; public class MainClass { public static void main(String[] args) throws InterruptedException { Person person = new Person(); person.sayName(); } }
想把Person的名称改成LiLei呢?instument有一个ClassFileTransformer接口,接口中只有一个唯一的方法.
byte[] transform( ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException;
加了agent后,每个被加载的class都会被检查一遍. classfileBuffer是类文件的字节码被当作字节码数组传递进来,返回值被改变后的class字节码,如果返回null,则字节码不被修改.在该方法中,字节码的任何改变都会在ClassLoader中重新生效.我们可以通过字节码增强的工具如ASM,Cglin,BCEL等工具进行字节码增强,改变原来class,执行新功能.当然目前的只针对被 AppClassLoader加载的类,如果更改BootstrapClassLoader,ExtClassLoader加载的类,则需求其他的修改,后面会举例.
下面用最简单的,重新加载一个class文件的方式实现字节码修改.修改后的新Person
public class Person { public void sayName() { System.out.println("I am LiLei"); } }
编译并命名成Person.class.new.
实现ClassFileTransformer接口
package instrumentexample; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.lang.instrument.ClassFileTransformer; import java.lang.instrument.IllegalClassFormatException; import java.security.ProtectionDomain; public class MethodTransformer implements ClassFileTransformer { public static final String newClassFilePath = "Person.class.new"; // 从新class文件中读取字节码 public static byte[] readBytesFromFile(String fileName) { try { File file = new File(fileName); InputStream is = new FileInputStream(file); byte[] bytes = new byte[(int) file.length()]; is.read(bytes); is.close(); return bytes; } catch (Exception e) { return null; } } // 实现接口,class字节码修改发生的地方 public byte[] transform(ClassLoader l, String className, Class<?> c, ProtectionDomain pd, byte[] b) throws IllegalClassFormatException { // 如果不是要修改的类,直接返回null if (!className.equals("instrumentexample/Person")) { return null; } return readBytesFromFile(newClassFilePath); } }
再在Agent里面添加该MethodTransformer
public class PremainModifyClass { public static void premain(String agentArgs, Instrumentation inst) throws ClassNotFoundException, UnmodifiableClassException { inst.addTransformer(new MethodTransformer()); } }
PremainModifyClass和MethodTransformer打包成agent.jar,执行跟例子1一样.
java6引入的新特性:JVM启动后动态 instrument、本地代码(native code)instrument,以及动态改变 classpath.
JVM启动后动态 instrument:在虚拟机初始化完成后(绝大部分的类库都已经被加载完毕),通过Java Tool API 中的 attach 方式,在运行过程中动态的实现instrumentation.
public static void agentmain (String agentArgs, Instrumentation inst); [1] public static void agentmain (String agentArgs); [2]
和premain()类似.
例子3:
public class Agentmain { public static void agentmain(String agentArgs, Instrumentation inst) { Class[] classes = inst.getAllLoadedClasses(); for (Class classstr : classes) { System.out.println("Class:" + classstr.getName() + ";ClassLoader:" + classstr.getClassLoader()); } } }
public class LoadAgentMain { public static void main(String[] args) throws AttachNotSupportedException, IOException, AgentLoadException, AgentInitializationException { // args[0]为JVM实例的pid,args[1]为agent jar的路径 VirtualMachine vm = VirtualMachine.attach(args[0]); vm.loadAgent(args[1]); } }
修改例子1:
public class MainClass { public static void main(String[] args) throws InterruptedException { Person person = new Person(); while (0 < 1) { } } }
jps命令行得到MainClass的pid, 打包AgentMain为agent.jar,修改Manifest文件
Agent-Class: instrumentexample.Agentmain
java -javaagent:agent.jar MainClass pid path;
从打印出来的结果可以看到,MainClass和Person类都已经被AppClassLoader加载完成了. 这个就是premain()方式和agentmain()方式的区别.一个是在JVM实例启动前执行instrumentation,一个是在启动后执行instrumentation.
如果在agentmain()里面改变字节码,就实现了运行时的instrumentation.
运行时改变 BootClassPath/SystemClassPath
上面我们改变的都是被AppClasssLoader加载的class,如果要改变BootstrapClassLoader加载的class呢?
可以设置一个虚拟机运行时的 boot class 加载路径(-Xbootclasspath)和 system class(-cp)加载路径.当然,我们在运行之后无法替换它。然而,我们也许有时候要需要把某些 jar 加载到 bootclasspath 之中,而我们无法应用上述两个方法。在 Java SE 6 之中,可以实现.premain/agantmain 函数里面通过 appendToBootstrapClassLoaderSearch或 appendToSystemClassLoaderSearch 来动态添加ClassLoader的加载路径.( 虚拟机的独特 ClassLoader 的工作方式,它会记载解析结果。比如,我们曾经要求读入某个类 someclass,但是失败了,ClassLoader 会记得这一点。即使我们在后面动态地加入了某一个 jar,含有这个类,ClassLoader 依然会认为我们无法解析这个类,与上次出错的相同的错误会被报告。 )
文献引用:
Java SE 6 新特性: Instrumentation 新功能 http://www.ibm.com/developerworks/cn/java/j-lo-jse61/index.html
相关推荐
### MATLAB Instrument Control Toolbox详解 #### 引言 MATLAB Instrument Control Toolbox是一款强大的工具包,它扩展了MATLAB的功能,使用户能够直接从MATLAB环境控制和读取各种测量仪器的数据。这一工具包对于...
《深入剖析Spring Instrument源码》 Spring框架是Java开发中不可或缺的一部分,其强大的功能和灵活的设计使得它在企业级应用中广泛应用。其中,Spring Instrument模块是Spring框架的一个重要组成部分,它提供了一种...
《深入解析Spring Instrument Tomcat源码》 Spring框架以其强大的功能和灵活性在Java开发领域占据着举足轻重的地位。而Spring Instrument Tomcat模块则是Spring框架中的一个重要组成部分,它为Tomcat服务器提供了类...
Multi-Instrument Pro 3.9.3最新版万用仪,Multi-Instrument万用仪将你的电脑变为一台多功能的虚拟仪器,如示波器,频谱分析仪、声谱仪,频率计声压计,振动计及LCR表,设备检测仪数据记录仪,信号发生器等仪器。
Multi-Instrument 3.2虚拟示波器最新版破_解补丁 使用方法: 复制MI破解.exe到你的安装文件夹,运行MI破解.exe启动破解,永远的21天使用期限,MI.EXE为运行原版. 测试方法: 修改系统时间,延后1各月,运行原版,使用天数...
Multi-Instrument 3.2虚拟示波器最新版的破解文件 使用方法: 复制MI破解.exe到你的安装文件夹,运行MI破解.exe启动破解,永远的21天使用期限,MI.EXE为运行原版. 测试方法: 修改系统时间,延后1各月,运行原版,使用天数...
### Instrument 苹果性能调试工具 #### 知识点概览 - **Instrument 工具简介** - **主要功能及用途** - **测试流程** - **常见问题与解决方法** ### Instrument 工具简介 Instrument 是苹果公司为开发者提供的一款...
《MATLAB 7.1.0 (R2007a) Instrument Control Toolbox 深度解析》 MATLAB,全称“Matrix Laboratory”,是一款强大的数学计算和数据分析软件,广泛应用于工程、科研和教育领域。而MATLAB 7.1.0(R2007a)版本则是其...
- **分辨率带宽**:影响频谱分辨率,越小的值可以获得更精细的频谱细节。 - **视频带宽**:决定频率分辨率,影响分析结果的准确性。 - **信号发生器** - **波形类型**:可以选择不同的波形输出,如正弦波、方波等...
spring-instrument-tomcat-3.2.9.RELEASE.jar
虚拟仪器(Virtual Instrument,简称VI)是现代测试测量领域中的一个重要概念,它结合了软件和硬件的优势,使得用户可以通过自定义的图形化编程界面来构建和控制测量系统。虚拟仪器的核心在于利用计算机的强大处理...
spring-instrument-4.3.8.RELEASE!!!!!!!!!!!
Instruments 是应用程序用来动态跟踪和分析 Mac OS X 和 iOS 代码的实用工具。 这是一个灵活而强大的工具,它让你可以跟踪一个或多个进程,并检查收集的数据。 这样,Instruments 可以帮你更好的理解应用程序和操作...
网上流传的破解都很麻烦, 每过一段时间都要从新破解, 而且界面有试用标记 该版本完整破解
首先,LabVIEW(Laboratory Virtual Instrument Engineering Workbench)是NI公司推出的一种基于G语言(Graphical Programming Language)的编程工具,其以图标和连线代替传统文本编程,降低了编程难度,尤其适合于...
Java开发工具包(JDK)中的Instrumentation接口是Java虚拟机(JVM)的一个强大特性,它允许在类被加载到内存之前对字节码进行动态修改。这种能力使得开发者可以实现诸如性能监控、内存分析、代码插桩等高级功能。...
spring-instrument-4.0.0.RELEASE.jar
### Multi-Instrument (万用仪) 3.2 虚拟仪器软件介绍及功能解析 #### 一、概述 Multi-Instrument(万用仪)3.2 是一款功能全面且强大的虚拟仪器软件,能够将用户的计算机转变为多种虚拟仪器,如示波器、频谱分析...
《GMS飞行器仪表助手详解》 在航空航天领域,精确的飞行仪器对于飞行安全至关重要。GMS(Generic Monitoring System)飞行器仪表控件是专为实现这一目标而设计的高级工具,它通过ActiveX控制技术,为飞行员和地面...