JVMTI

什么是JVMTI

JVM Tool Interface简称JVMTI是一组对外接口,通过这组接口能够实现,获取虚拟机运行状态、线程剖析、监控、调试、覆盖率剖析等性能。

JVMTIAgent

什么是JVMTIAgent

为了应用JVMTI提供的对外接口,个别采纳Agent形式来实现JVMTI提供的对外接口,JVMTIAgent相似于c语言的动静库的概念。

实现形式

Java1.5之前实现一个Agent只能通过原生的c/c++来实现Agent,在Java1.5之后提供了instrumentagent,也叫做JPLISAgent(Java Programming Language Instrumentation Services Agent)专门用于Java形式。

启动形式

Agent有两种启动形式

  • 第一种是在jvm启动的时候,指定agent程序的地位来启动。
  • 另外一种形式是jvm曾经在运行了,应用attach的形式到指标过程外面。在java1.5的时候只反对jvm启动,在java1.6的时候反对attach的形式启动,在jvmtool.jar外面提供了工具VirtualMachine来帮忙启动agent

Instrument

什么是Instrument

Instrument提供了为Java编程语言插入代码的服务,Instrumentation是在办法中增加字节码,以便收集应用的数据,因为这些扭转是增加字节码,不会批改程序的状态或者行为。比方监视器代码、探查器、覆盖率分析器和事件记录器。

Instrument只是提供插入代码服务,在办法中增加字节码,至于具体的字节码操作,是由字节码操作工具来实现的,常见的字节码操作工具包含:CGLIBJavassistASM等。

获取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");