Java Agent(二)Attach机制及运行时加载agent

引入

从JDK6以来,我们可以通过agentmain方法进行对JVM中运行的程序进行监控。然鹅不像premain由JVM启动时被调用,这个agentmain方法调用的时机是什么?我们又如何发送一个信号给目标虚拟机?

简介

agentmain方法调用的时机,自然是我们在一个进程中所触发的,它通过发送信号告诉另外一个进程,也就是目标JVM进程,此时要加载我们指定的agent。这个过程中,涉及进程间通信,在进程间传递命令与参数。 JDK提供了一种方式去实现这种通信:Attach API。它的核心类为VirtualMachine,其中的attach方法利用了UDS(Unix Domain socket)原理实现了Linux中进程间的通信。

Demo

书接上文

  1. Agent类新增agentmain方法
public class Agent {
          
   

    public static void premain(String args, Instrumentation ins) {
          
   
        System.out.printf("Pre-Main called , args : " + args);
        ins.addTransformer(new Transformer());
    }

    public static void agentmain(String args, Instrumentation ins) {
          
   
        System.out.println("Agent-Main called , args : " + args);

        Class<?>[] cls = ins.getAllLoadedClasses();
        for (Class<?> cl : cls) {
          
   
            if (cl.getName().equals("com.example.javaagent.demo.controller.Demo")) {
          
   
                System.out.println("Find Demo.Class ! ");
                String fileName = "... .../Demo.class";
                byte[] bytes = getBytesFromFile(fileName);
                ClassDefinition classDefinition = new ClassDefinition(cl, bytes);
                try {
          
   
                    ins.redefineClasses(classDefinition);
                } catch (ClassNotFoundException e) {
          
   
                    e.printStackTrace();
                } catch (UnmodifiableClassException e) {
          
   
                    e.printStackTrace();
                }
            }
        }
    }

    public static byte[] getBytesFromFile(String fileName) {
          
   
        File file = new File(fileName);
        Long filelength = file.length();
        byte[] filecontent = new byte[filelength.intValue()];
        try {
          
   
            FileInputStream in = new FileInputStream(file);
            in.read(filecontent);
            in.close();
        } catch (FileNotFoundException e) {
          
   
            e.printStackTrace();
        } catch (IOException e) {
          
   
            e.printStackTrace();
        }
        return filecontent;
    }
}
  1. 将Demo工程的JVM参数-javaagent去掉,正常启动java进程 3.写一个attach方法并运行,其中的pid为Demo工程的java进程号
public static void main(String[] args) {
          
   
        try {
          
   
            VirtualMachine virtualMachine = VirtualMachine.attach(String.valueOf(pid));
            virtualMachine.loadAgent("... .../agent.jar=testArg");
        } catch (Exception e) {
          
   
            e.printStackTrace();
        }
    }

运行后Demo工程将输出两行日志:

Agent-Main called , args : testArg
Find Demo.Class !

此时再请求接口,类字节码已被转换:

经验分享 程序员 微信小程序 职场和发展