认识java agent

java agent

agent

java agent是在JDK1.5之后引入的,是main方法之前的拦截器;其作用是在main之前之前执行agent中的逻辑。

假设 A 是主要业务逻辑应用所在进程; B 是要拦截在A的main方法之前的 agent。

那么agent有两种启动方式:

  • (1)B在A之前启动:通过 启动A时添加vm参数 -javaagent:xxxx.jar

    a. xxxx.jar包含了自己实现的agent类,实现规范中一种形式如下:

    public static void premain(String agentOps, Instrumentation inst){  
        //TODO 
    }
    

    b. xxxx.jar包含的资源配置文件Manifest中需要指明premain所在的类:

    Premain-Class: com.mypackage.MyAgent
    
  • (2)B在A之后启动:通过 VirtualMachine 的 attach 和 loadAgent 方法实现。

    VirtualMachine类所在包为:com.sun.tools.attach;相关代码:

    try{
        String pid = fetchPidId(); //获取到java 的 pid(各种方式)
        if( pid != null ){
            vm = VirtualMachine.attach( pid );    // 获取attach到的vm对象实例                                 
            vm.loadAgent(agentJarFilePath, args); // 加载agent
        }
    }finally {
        vm.detach();
    }
    

    a. agentJarFilePath包含了自己实现的agent类,实现规范中一种形式如下:

    public static void agentmain(String args, Instrumentation inst) {
        //TODO
    }
    

    b. agentJarFilePath包含了资源配置文件Manifest中需要指明agentmain所在的类:

    Agent-Class: war.Hatch
    

tips
资源描述文件Manifest里面agent相关的额外配置项如下:

  • Can-Redefine-Classes

    如果此值为true,可以修改已经加载的class的字节码。

  • Can-Retransform-Classes

    如果此值为true,表示支持重复修改class的字节码。这个和redefine的区别是,transform只是一个filter,没有修改本源字节码。

instrument

agent介绍中,我们可以看到instrument的身影,用来增强字节码。

instrument中可以:

1. 在 A 所在的JVM进程的BootstrapClassLoader 或者 SystemClassLoader(AppClassLoader)中加载额外的jar包。
2. 可以获得 A 中已经加载的所有的class类。
3. 可以添加 ClassFileTransformer 类来进行class文件(字节码)的修改。
    instrument.add

更多请参考API。这里有一篇比较好的理解,强烈推荐。

实践经验
  • 因为com.sun.tools.attach.VirtualMachine在tools.jar中,所以在(2)情况下要调用其attach和loadAgent的时候,需要在classpath中添加tools.jar。形如:

    java -classpath {JAVA_HOME}/lib/tools.jar:myTransport.jar Transport
    

    Transport 中的main方法包含了(2)中的示例代码。

  • 加入是用maven打包的推荐使用 maven-assembly-plugin,用其来管理资源管理文件配置项,示例配置如下:

    <manifestEntries>
        <Premain-Class>agent</Premain-Class>
        <Agent-Class>agent</Agent-Class>
        <Can-Redine-Classes>true</Can-Redine-Classes>
        <Can-Retransform-Classes>true</Can-Retransform-Classes>
    </manifestEntries>
    
  • 获取PID的示例命令:

    ps ax | grep 'java' | cut -d " " -f2
    
agent技术已知商业价值
  • 性能剖析:Btrace
  • 错误诊断:Btrace, Greys
  • AOP切面技术:Spring