`
huoyanxueren
  • 浏览: 39805 次
  • 性别: Icon_minigender_1
  • 来自: 杭州
社区版块
存档分类
最新评论

Instrument小窥

 
阅读更多

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 Instrument Control Toolbox是一款强大的工具包,它扩展了MATLAB的功能,使用户能够直接从MATLAB环境控制和读取各种测量仪器的数据。这一工具包对于...

    spring-instrument源码

    《深入剖析Spring Instrument源码》 Spring框架是Java开发中不可或缺的一部分,其强大的功能和灵活的设计使得它在企业级应用中广泛应用。其中,Spring Instrument模块是Spring框架的一个重要组成部分,它提供了一种...

    spring-instrument-tomcat源码

    《深入解析Spring Instrument Tomcat源码》 Spring框架以其强大的功能和灵活性在Java开发领域占据着举足轻重的地位。而Spring Instrument Tomcat模块则是Spring框架中的一个重要组成部分,它为Tomcat服务器提供了类...

    Multi-Instrument Pro 3.9.3万用仪

    Multi-Instrument Pro 3.9.3最新版万用仪,Multi-Instrument万用仪将你的电脑变为一台多功能的虚拟仪器,如示波器,频谱分析仪、声谱仪,频率计声压计,振动计及LCR表,设备检测仪数据记录仪,信号发生器等仪器。

    Multi-Instrument 3.2虚拟示波器最新版破_解补丁

    Multi-Instrument 3.2虚拟示波器最新版破_解补丁 使用方法: 复制MI破解.exe到你的安装文件夹,运行MI破解.exe启动破解,永远的21天使用期限,MI.EXE为运行原版. 测试方法: 修改系统时间,延后1各月,运行原版,使用天数...

    Multi-Instrument 3.2虚拟示波器最新版破解补丁.zip

    Multi-Instrument 3.2虚拟示波器最新版的破解文件 使用方法: 复制MI破解.exe到你的安装文件夹,运行MI破解.exe启动破解,永远的21天使用期限,MI.EXE为运行原版. 测试方法: 修改系统时间,延后1各月,运行原版,使用天数...

    Instrument 苹果性能调试工具

    ### Instrument 苹果性能调试工具 #### 知识点概览 - **Instrument 工具简介** - **主要功能及用途** - **测试流程** - **常见问题与解决方法** ### Instrument 工具简介 Instrument 是苹果公司为开发者提供的一款...

    matlab 7.1.0(R2007a) 的Instrument Control Toolbox

    《MATLAB 7.1.0 (R2007a) Instrument Control Toolbox 深度解析》 MATLAB,全称“Matrix Laboratory”,是一款强大的数学计算和数据分析软件,广泛应用于工程、科研和教育领域。而MATLAB 7.1.0(R2007a)版本则是其...

    VIRTINS Multi-Instrument(万用仪) 3.2 使用说明书 手册

    - **分辨率带宽**:影响频谱分辨率,越小的值可以获得更精细的频谱细节。 - **视频带宽**:决定频率分辨率,影响分析结果的准确性。 - **信号发生器** - **波形类型**:可以选择不同的波形输出,如正弦波、方波等...

    spring-instrument-tomcat-3.2.9.RELEASE.jar

    spring-instrument-tomcat-3.2.9.RELEASE.jar

    virtual instrument 外文翻译

    虚拟仪器(Virtual Instrument,简称VI)是现代测试测量领域中的一个重要概念,它结合了软件和硬件的优势,使得用户可以通过自定义的图形化编程界面来构建和控制测量系统。虚拟仪器的核心在于利用计算机的强大处理...

    spring-instrument-4.3.8.RELEASE

    spring-instrument-4.3.8.RELEASE!!!!!!!!!!!

    instrument

    Instruments 是应用程序用来动态跟踪和分析 Mac OS X 和 iOS 代码的实用工具。 这是一个灵活而强大的工具,它让你可以跟踪一个或多个进程,并检查收集的数据。 这样,Instruments 可以帮你更好的理解应用程序和操作...

    Multi-Instrument V3.2 简化版

    网上流传的破解都很麻烦, 每过一段时间都要从新破解, 而且界面有试用标记 该版本完整破解

    national instrument labview modbus

    首先,LabVIEW(Laboratory Virtual Instrument Engineering Workbench)是NI公司推出的一种基于G语言(Graphical Programming Language)的编程工具,其以图标和连线代替传统文本编程,降低了编程难度,尤其适合于...

    jdk 5 instrument学习

    Java开发工具包(JDK)中的Instrumentation接口是Java虚拟机(JVM)的一个强大特性,它允许在类被加载到内存之前对字节码进行动态修改。这种能力使得开发者可以实现诸如性能监控、内存分析、代码插桩等高级功能。...

    spring-instrument-4.0.0.RELEASE.jar

    spring-instrument-4.0.0.RELEASE.jar

    VIRTINS Multi-Instrument虚拟示波器HANTEK6022插件

    VIRTINS Multi-Instrument这款软件功能还是很好用的,以前只能用用声卡,当然有高级示波器的就跳过了,在电脑上操作还是方便些,测量什么的鼠标一拖就可以了,还有软件滤波,波形放大等待功能,在带宽要求不高的时候...

    multi instrument

    ### Multi-Instrument (万用仪) 3.2 虚拟仪器软件介绍及功能解析 #### 一、概述 Multi-Instrument(万用仪)3.2 是一款功能全面且强大的虚拟仪器软件,能够将用户的计算机转变为多种虚拟仪器,如示波器、频谱分析...

Global site tag (gtag.js) - Google Analytics