Java Attach API是Java SE 6及以上版本中提供的一组API,其作用是允许在运行中的Java虚拟机中注入代码或从已经运行的Java虚拟机中获取信息。其主要用于与Java虚拟机进行连接和信息交互的场景,比如:调试、性能监控和诊断分析等。
完整使用攻略:
- 下载tools.jar
Java Attach API位于tools.jar中,需要从Java开发套件中提取该文件才能使用。首先下载和安装Java Development Kit (JDK),然后将tools.jar从JDK的安装目录(%JAVA_HOME%/lib/)中复制到自定义目录中,以备后用。
- 加载Java Attach API
Java Attach API最重要的类是VirtualMachine,它表示本地虚拟机或远程虚拟机,可以用来连接、启动和管理Java虚拟机。为了加载VirtualMachine类,需要把tools.jar中的对应类文件添加到classpath或直接使用它的绝对路径:
// 加载VirtualMachine类
URLClassLoader loader =
new URLClassLoader(new URL[]{new File("tools.jar").toURI().toURL()});
Class<?> vmClass = Class.forName("com.sun.tools.attach.VirtualMachine", true, loader);
- 连接Java虚拟机
连接本地虚拟机或远程虚拟机都可以使用VirtualMachine类的attach方法。attach方法会返回一个VirtualMachine对象,用于后续的交互。
// 获取本地虚拟机并连接
VirtualMachine vm = VirtualMachine.attach("pid");
pid是Java虚拟机的进程id,可以通过jps命令或Java Management Extensions(JMX)获取。
- 注入代码
Java Attach API通过Instrumentation接口提供了在Java虚拟机运行过程中注入JVM Agent的能力。注入的Agent可以修改字节码或者使用JVMTI实现代码植入的目的。具体来说,Instrumentation的两个重要方法是addTransformer和retransformClasses。这两个方法都需要在Agent或者premain后重新启动函数在JVM生命周期中执行,在启动时得到的所有加载类将被注册,但只有addTransformer的函数会被用于映射文件(即不改变原始字节码)。除此之外,加注入JVM Agent操作需要启动前另一个Agent操作,重写方法转换处理。
下面是一个简单的Agent,它会在启动时输出所有类的名称和版本:
public static class ClassVersioner implements ClassFileTransformer {
public byte[] transform(ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.out.println("Transforming " + className);
return classfileBuffer;
}
}
public static void premain(String args, Instrumentation inst) {
System.out.println("Premain " + args);
inst.addTransformer(new ClassVersioner());
}
- 获取Java虚拟机信息
Java Attach API也能够用于获取Java虚拟机的信息,比如系统属性、命令行参数、线程堆栈等。通过VirtualMachine对象的getSystemProperties和getAgentProperties方法可以获取系统属性和Agent属性。
示例1:获取Java虚拟机系统属性
// 获取Java虚拟机基本信息
Properties props = vm.getSystemProperties();
for (Map.Entry<Object, Object> entry : props.entrySet()) {
System.out.println(entry.getKey() + "=" + entry.getValue());
}
示例2:获取Java虚拟机命令行参数
// 获取Java虚拟机命令行参数
String[] args = vm.getAgentProperties().getProperty("sun.java.command").split(" ");
for (String arg : args) {
System.out.println(arg);
}
以上就是Java Attach API的使用攻略,其涉及到了动态注入代码和获取Java虚拟机信息的场景。