JVMTI
什么是JVMTI
JVM Tool Interface
简称JVMTI
是一组对外接口,通过这组接口能够实现,获取虚拟机运行状态、线程剖析、监控、调试、覆盖率剖析等性能。
JVMTIAgent
什么是JVMTIAgent
为了应用JVMTI
提供的对外接口,个别采纳Agent
形式来实现JVMTI
提供的对外接口,JVMTIAgent
相似于c
语言的动静库的概念。
实现形式
在Java1.5
之前实现一个Agent
只能通过原生的c/c++
来实现Agent
,在Java1.5
之后提供了instrument
的agent
,也叫做JPLISAgent(Java Programming Language Instrumentation Services Agent)
专门用于Java
形式。
启动形式
Agent
有两种启动形式
- 第一种是在
jvm
启动的时候,指定agent
程序的地位来启动。 - 另外一种形式是jvm曾经在运行了,应用
attach
的形式到指标过程外面。在java1.5
的时候只反对jvm
启动,在java1.6
的时候反对attach
的形式启动,在jvm
的tool.jar
外面提供了工具VirtualMachine
来帮忙启动agent
。
Instrument
什么是Instrument
Instrument
提供了为Java
编程语言插入代码的服务,Instrumentation
是在办法中增加字节码,以便收集应用的数据,因为这些扭转是增加字节码,不会批改程序的状态或者行为。比方监视器代码、探查器、覆盖率分析器和事件记录器。
Instrument
只是提供插入代码服务,在办法中增加字节码,至于具体的字节码操作,是由字节码操作工具来实现的,常见的字节码操作工具包含:CGLIB
、Javassist
、ASM
等。
获取Instrumentation实例
指定接管类
要获取Instrumentation
实例,首先要指定将Instrumentation
实例传递给哪个类,有两种形式来指定传递给这个类。
第一种形式是在配置文件
resource\META_INF\MANIFEST.MF
中指定。Manifest-Version: 1.0Can-Redefine-Classes: trueCan-Retransform-Classes: truePremain-Class: com.lee.agent.PreMainAgentAgent-Class: com.lee.agent.PreMainAgent
第二种形式是在
pom
文件中指定,实质上也是在配置MANIFEST.MF
文件<plugin> <excutions> <excution> <archive> <manifestFile> <Premain-Class>com.lee.agent.PreMainAgent</Premain-Class> <Agent-Class>com.lee.agent.PreMainAgent</Agent-Class> </manifestFile> </archive> </excution> </excutions></plugin>
指定接管办法
当
JVM
以指定代理类的形式启动,在这种状况下Instrumentation
实例被传给代理类的premain
办法;public static void premain(String agentArgs, Instrumentation inst);public static void premain(String agentArgs);
当
JVM
启动后,以attach
的形式指定代理类,在这种状况下Instrumentation
实例被传递给代理类的agentmain
办法。public static void agentmain(String agentArgs, Instrumentation inst);public static void agentmain(String agentArgs);
示例代码
整体流程图示
目标程序
目标程序是被操作的程序,被批改的是指标类TargetClass
。
public class Demo { public static void main(String[] args) throws Exception { TargetClass targetClass = new TargetClass(); targetClass.targetMethod(); }}public class TargetClass { public String targetMethod() { System.out.println("执行测试形式"); return "return"; }}
Agent程序
public class PreMainAgent { /** * 指定agentjar包启动,Instrument实例会传递给这个办法 */ public static void premain(String agentArgs, Instrumentation inst){ customLogic(inst); } /** * attach办法启动,Instrument实例会传递给这个办法 */ public static void agentmain(String agentArgs, Instrumentation inst){ customLogic(inst); } private static void customLogic(Instrumentation inst){ inst.addTransformer(new MyClassTransformer(), true); }}class MyClassTransformer implements ClassFileTransformer { @Override public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) { final ClassPool classPool = ClassPool.getDefault(); CtClass clazz; CtMethod ctMethod; try { if ("com/lee/TargetClass".equals(className)){ clazz = classPool.get("com.lee.TargetClass"); ctMethod = clazz.getDeclaredMethod("targetMethod"); ctMethod.insertBefore("System.out.println(\"****************\");"); byte[] byteCode = clazz.toBytecode(); clazz.detach(); return byteCode; } } catch (Exception e) { e.printStackTrace(); } return null; }}
启动
- 先将
agent
我的项目打包成一个jar
包,agent.jar
两种启动形式
- 在启动目标程序的时候指定
agent
的地位:-javaagent:jar包门路\Jagent.jar
以
attach
形式启动// project1启动的pidVirtualMachine vm = VirtualMachine.attach("1856");vm.loadAgent("jar包门路\Jagent.jar");
- 在启动目标程序的时候指定