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.0 Can-Redefine-Classes: true Can-Retransform-Classes: true Premain-Class: com.lee.agent.PreMainAgent Agent-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 启动的 pid VirtualMachine vm = VirtualMachine.attach("1856"); vm.loadAgent("jar 包门路 \Jagent.jar");
- 在启动目标程序的时候指定