catalog

1. instrucment与Attach API
2. BTrace: VM Attach的两种方式
3. Sun JVM Attach API

1. instrucment与Attach API

JDK5中增加了一个包java.lang.instrucment,能够对JVM底层组件进行访问。在JDK 5中,Instrument 要求在运行前利用命令行参数或者系统参数来设置代理类,在实际的运行之中,虚拟机在初始化之时(在绝大多数的 Java 类库被载入之前),instrumentation的设置已经启动,并在虚拟机中设置了回调函数,检测特定类的加载情况,并完成实际工作
​在Java5中,开发基于Instrucment的应用,需要以下几个步骤

1. 编写premain函数
​2. jar文件打包
​3. 运行agent 

但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理,这样实际上限制了instrument的应用。而Java SE 6的新特性改变了这种情况,通过Java Tool API中的attach方式,我们可以很方便地在运行过程中动态地设置加载代理类,以达到instrumentation的目的
​在JDK6中,针对这点做了改进,开发者可以在main开始执行以后,再开启自己的Instrucment程序
Attach API不是Java的标准API,而是Sun公司提供的一套扩展API,用来向目标JVM"附着"(Attach)代理工具程序的。有了它,开发者可以方便的监控一个JVM,运行一个外加的代理程序,Sun JVM Attach API功能上非常简单,仅提供了如下几个功能

1. 列出当前所有的JVM实例描述
2. Attach到其中一个JVM上,建立通信管道
3. 让目标JVM加载Agent

Relevant Link:

http://iamzhongyong.iteye.com/blog/1843558

2. BTrace: VM Attach的两种方式

BTrace的特点之一就是可以动态Attach到一个运行的JVM进程上,然后根据BTrace脚本来对目标JVM进行相应的操作
JVM的 Attach有两种方式

1. 指定javaagent参数
2. 运行时动态attach

0x1: 指定javaagent参数

这种方式的特点就是在目标JVM启动时,就确定好了要加载什么样的代理对象,例如

java -javaagent:xxxx.jar TestMain

TestMain.java

package test;public class TestMain
{ public static void main(String[] args) throws InterruptedException{System.out.println("Hello");}}

TestAgent.java

package test;import java.lang.instrument.Instrumentation;
import java.io.*;public class TestMain
{ public static void agentmain(String args, Instrumentation inst) throws Exception {System.out.println("Args:" + args);}public static void premain(String args, Instrumentation inst) throws Exception {System.out.println("Pre Args:" + args);Class[] classes = inst.getAllLoadedClasses();for (Class clazz : classes) {System.out.println(clazz.getName());}}
}

TestAgent类比较简单,最终它会在目标类的Main方法执行之前,执行premain方法,其主要动作是将以及加载的类打印出来。 我们需要将这个类打包成jar文件,以便在目标JVM启动时候,以参数形式指定给它。打成jar的同时,设定MANIFEST.MF文件的内容。告知目标JVM该如何处理

Agent-Class: TestAgent
Premain-Class: TestAgent
Can-Redine-Classes: true
Can-Retransform-Classes: true

用jar命令将TestAgent打包

1. 编译TestAgent
javac TestAgent.java2. jar打包
jar cvmf MANIFEST.MF xxx.jar TestAgent.class

启动TestMain,并设置javaagent参数

1. 编译TestMain
javac TestMain.java 2. 启动TestMain
java -javaagent:xxx.jar TestMain

0x2: 动态Attach,load指定Agent

这种方式与之前指定参数的不同在于,其可以在JVM已经运行的情况下,动态的附着上去,并可以动态加载agent
TestMain.java

public class TestMain
{public static void main(String[] args) throws InterruptedException {  while(true){  Thread.sleep(10000);  new Thread(new WaitThread()).start();  }  }  static class WaitThread implements Runnable {  @Override  public void run() {  System.out.println("Hello"); }       }
}

TestAgent.java

import java.lang.instrument.Instrumentation;
import java.io.*;public class TestAgent
{ public static void agentmain(String args, Instrumentation inst) throws Exception {System.out.println("Args:" + args);}public static void premain(String args, Instrumentation inst) throws Exception {System.out.println("Pre Args:" + args);Class[] classes = inst.getAllLoadedClasses();for (Class clazz : classes) {System.out.println(clazz.getName());}}
}

动态加载agent的情况下,被调用的是agentmain方法, 其会在JVMload的时候,被调用
MANIFEST.MF

Agent-Class: TestAgent
Premain-Class: TestAgent
Can-Redine-Classes: true
Can-Retransform-Classes: true

将类打包为jar包

1. 编译TestAgent
javac TestAgent.java2. jar打包
jar cvmf MANIFEST.MF xxx.jar TestAgent.class

动态附着到对应的JVM需要使用到JDK的Attach API
Main.java

import com.sun.tools.attach.VirtualMachine;public class Main
{  public static void main(String[] args) throws Exception{  VirtualMachine vm = null;  String agentjarpath = "C:/Users/zhenghan.zh/Desktop/新建文件夹/xxx.jar"; //agentjar路径  vm = VirtualMachine.attach("9730");//目标JVM的进程ID(PID)  vm.loadAgent(agentjarpath, "This is Args to the Agent.");  vm.detach();  }
}

一旦运行这个Main方法, 其就会动态的附着到我们对应的JVM进程中,并为目标JVM加载我们指定的Agent,以达到我们想做的事情, 比如BTrace就为在附着到目标JVM后,开启一个ServerSocket,以便达到与目标进程通讯的目的

Relevant Link:

http://ivanzhangwb.github.io/btrace-vm-attach-api/ 

3. Sun JVM Attach API

Sun JVM Attach API是Sun JVM中的一套非标准的可以连接到JVM上的API,从JDK6开始引入,除了Solaris平台的Sun JVM支持远程的Attach,在其他平台都只允许Attach到本地的JVM上

0x1: 列出当前所有的JVM实例描述

package test;
import java.util.List;import com.sun.tools.attach.VirtualMachine;
import com.sun.tools.attach.VirtualMachineDescriptor;public class Test
{public static void main(String[] args) {List<VirtualMachineDescriptor> list = VirtualMachine.list();  for (VirtualMachineDescriptor vmd : list)  {  System.out.println("pid:" + vmd.id() + ":" + vmd.displayName());  }  }}
//tools.jar needs to be added to the IDE's library path and the program's classpath. The tools.jar file is found in the JDK's lib directory.

0x2: Attach到特定进程的JVM上,并加载Agent

//Attach到JVM上
VirtualMachine virtualmachine = VirtualMachine.attach(pid);
//加载Agent
String javaHome = virtualmachine.getSystemProperties().getProperty("java.home");
String agentPath = javaHome + File.separator + "jre" + File.separator + "lib" + File.separator + "management-agent.jar");
File file = new File(agentPath);
if(!file.exists())
{  agentPath = javaHome + File.separator + "lib" + File.separator + "management-agent.jar";  file = new File(agentPath);  if(!file.exists())  throw new IOException("Management agent not found");  }
}  agentPath = file.getCanonicalPath();
try
{  virtualmachine.loadAgent(agentPath, "com.sun.management.jmxremote");
}
catch(AgentLoadException e)
{  throw new IOException(e);
}
catch(AgentInitializationException agentinitializationexception)
{  throw new IOException(e);
}
Properties properties = virtualmachine.getAgentProperties();
address = (String)properties.get("com.sun.management.jmxremote.localConnectorAddress");
virtualmachine.detach(); 

0x3: Attach API底层实现(windows)

\openjdk\jdk\src\windows\classes\sun\tools\attach\WindowsAttachProvider.java

public VirtualMachine attachVirtualMachine(String vmid) throws AttachNotSupportedException, IOException
{checkAttachPermission();// AttachNotSupportedException will be thrown if the target VM can be determined// to be not attachable.
    testAttachable(vmid);return new WindowsVirtualMachine(this, vmid);
}

\openjdk\jdk\src\windows\classes\sun\tools\attach\WindowsVirtualMachine.java

WindowsVirtualMachine(AttachProvider provider, String id) throws AttachNotSupportedException, IOException
{//继承HotSpotVirtualMachine
    super(provider, id);int pid;try {pid = Integer.parseInt(id);} catch (NumberFormatException x) {throw new AttachNotSupportedException("Invalid process identifier");}//先连接上目标JVMhProcess = openProcess(pid);// The target VM might be a pre-6.0 VM so we enqueue a "null" command// which minimally tests that the enqueue function exists in the target// VM.try {enqueue(hProcess, stub, null, null);} catch (IOException x) {throw new AttachNotSupportedException(x.getMessage());}
}

WindowsVirtualMachine继承HotSpotVirtualMachine,先看看HotSpotVirtualMachine的loadAgent方法
\openjdk\jdk\src\share\classes\sun\tools\attach\HotSpotVirtualMachine.java

/*
* Load JPLIS agent which will load the agent JAR file and invoke
* the agentmain method.
*/
public void loadAgent(String agent, String options) throws AgentLoadException, AgentInitializationException, IOException
{String args = agent;if (options != null) {args = args + "=" + options;}try {loadAgentLibrary("instrument", args);} catch (AgentLoadException x) {throw new InternalError("instrument library is missing in target VM");} catch (AgentInitializationException x) {/** Translate interesting errors into the right exception and* message (FIXME: create a better interface to the instrument* implementation so this isn't necessary)*/int rc = x.returnValue();switch (rc) {case JNI_ENOMEM:throw new AgentLoadException("Insuffient memory");case ATTACH_ERROR_BADJAR:throw new AgentLoadException("Agent JAR not found or no Agent-Class attribute");case ATTACH_ERROR_NOTONCP:throw new AgentLoadException("Unable to add JAR file to system class path");case ATTACH_ERROR_STARTFAIL:throw new AgentInitializationException("Agent JAR loaded but agent failed to initialize");default :throw new AgentLoadException("Failed to load agent - unknown reason: " + rc);}}
}

loadAgentLibrary("instrument", args);

/*
* Load agent library
* If isAbsolute is true then the agent library is the absolute path
* to the library and thus will not be expanded in the target VM.
* if isAbsolute is false then the agent library is just a library
* name and it will be expended in the target VM.
*/
private void loadAgentLibrary(String agentLibrary, boolean isAbsolute, String options) throws AgentLoadException, AgentInitializationException, IOException
{InputStream in = execute("load",agentLibrary,isAbsolute ? "true" : "false",options);try {int result = readInt(in);if (result != 0) {throw new AgentInitializationException("Agent_OnAttach failed", result);}} finally {in.close();}
}

可以看到,Java在Attach到目标进行后,调用execute让目标进行加载Agent类,我们继续分析execute的实现方式,可以看到,JVM进程间通信是JVM Attach API的核心,JVM自身就预留了执行来自Attach进程的指令接口
\openjdk\jdk\src\windows\classes\sun\tools\attach\WindowsVirtualMachine.java

InputStream execute(String cmd, Object ... args) throws AgentLoadException, IOException
{assert args.length <= 3;        // includes null// create a pipe using a random nameint r = (new Random()).nextInt();String pipename = "\\\\.\\pipe\\javatool" + r;long hPipe = createPipe(pipename);// check if we are detached - in theory it's possible that detach is invoked// after this check but before we enqueue the command.if (hProcess == -1) {closePipe(hPipe);throw new IOException("Detached from target VM");}try {// enqueue the command to the process
        enqueue(hProcess, stub, cmd, pipename, args);// wait for command to complete - process will connect with the// completion status
        connectPipe(hPipe);// create an input stream for the pipePipedInputStream is = new PipedInputStream(hPipe);// read completion statusint status = readInt(is);if (status != 0) {// special case the load command so that the right exception is thrownif (cmd.equals("load")) {throw new AgentLoadException("Failed to load agent library");} else {throw new IOException("Command failed in target VM");}}// return the input streamreturn is;} catch (IOException ioe) {closePipe(hPipe);throw ioe;}
}

JVM的execute方法中调用了大量native方法,并且从代码中可以看出,JVM Attach的进程间通信使用了管道进行通信

Relevant Link:

http://ayufox.iteye.com/blog/655761
http://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/com/sun/tools/attach/VirtualMachine.html
http://docs.oracle.com/javase/7/docs/jdk/api/attach/spec/index.html 

Copyright (c) 2015 LittleHann All rights reserved

Java Attach API相关推荐

  1. jvm attach api

    文章目录 1. 简介 2.主要接口与类 2.1 class VirtualMachine 2.1.1 概述 2.1.2 方法 2.1.3 使用示例 2.2 Class AttachPermission ...

  2. 我也没想到,Java开发 API接口可以不用写 Controller了

    大家好,我是小富~ 今天介绍我正在用的一款高效敏捷开发工具magic-api,顺便分享一点工作中使用它的心得 缘起 先说一下我为什么会使用这个工具? 最近新启动一个项目,业务并不算复杂,那种典型的管理 ...

  3. Java的API及Object类、String类、字符串缓存区

    Java 的API(API: Application(应用) Programming(程序) Interface(接口)) Object:Object类是Java语言中的根类,即所有类的父类. equ ...

  4. Java Persistence API中的FetchType LAZY和EAGER之间的区别?

    我是Java Persistence API和Hibernate的新手. Java Persistence API中的FetchType.LAZY和FetchType.EAGER什么区别? #1楼 我 ...

  5. Java 常用API的运用,效率及技巧

    1.     Java面向对象基本概念 2.     System 3.     String, StringBuffer 4.     数值,字符,布尔对象与简单类型的操作 5.     Class ...

  6. mq 接口 java_Rabbitmq Java Client Api详解

    AMQP AMQP协议是一个高级抽象层消息通信协议,RabbitMQ是AMQP协议的实现. 基础概念快速入门 每个rabbitmq-server叫做一个Broker,等着tcp连接进入. 在rabbi ...

  7. 关于 Java Collections API 您不知道的 5 件事--转

    第 1 部分 http://www.ibm.com/developerworks/cn/java/j-5things2.html 对于很多 Java 开发人员来说,Java Collections A ...

  8. JAVA EXCEL API详解

    2019独角兽企业重金招聘Python工程师标准>>> 使用Windows操作系统的朋友对Excel(电子表格)一定不会陌生,但是要使用Java语言来操纵Excel文件并不是一件容易 ...

  9. es dsl java api_求帮助将dsl翻译为java的API

    前面的query都会写java的api,但是后面的aggs的嵌套就不知道怎么写了.es版本为5.3.3 GET /realty_assistant/phoneinoutcallrecord/_sear ...

最新文章

  1. 深入理解分布式技术 - Paxos 算法解读
  2. 2014-06-25nbsp;20:39
  3. jquery sleep函数
  4. gitservergitlab之搭建和使用
  5. 《OpenACC并行程序设计:性能优化实践指南》一 3.1 性能分析技术和术语
  6. 云和恩墨大讲堂丨PostgreSQL逻辑复制案例分享
  7. jquery 时间相减获取天数_Js中处理日期加减天数
  8. shell读取文件的每一行
  9. express4.x中文文档
  10. 裘宗燕:C/C++ 语言中的表达式求值
  11. unity3d 求两个点长度_Unity3D实现体积光
  12. python 类装饰器和继承_python装饰器、继承、元类、mixin,四种給类动态添加类属性和方法的方式(一)...
  13. 邮箱app哪个好用 手机邮件软件排行榜
  14. 学以致提高学生操作计算机能力,学用结合,学以致用
  15. 傅里叶变换时域和频域之间的对应关系
  16. opencv 识别微信登录验证滑动块位置
  17. 大数据存储峰会4月9日深圳开幕
  18. 谷歌gmail注册入口_如何阻止Gmail将事件添加到Google日历
  19. 想要穿衣服好看,这4处肌肉一定要练好
  20. 迷宫生成算法和迷宫寻路算法

热门文章

  1. 原生及jq方式使用ajax
  2. 第一阶段冲刺(第七天)
  3. navicat 或者workbench 无法连接127.0.0.1(61)的解决方法
  4. 单独使用modelsim进行仿真
  5. 超炫的iphone应用UI/UX设计赏析
  6. poj 2696 A Mysterious Function
  7. java 终结此段代码并重新运行_Java垃圾回收
  8. js 获取td高度_JS或jQuery获取宽高度
  9. 虚拟化运维工具医院解决方案
  10. 通孔的作用是什么linux,电路板空洞的作用是什么 如何区分PTH与NPTH两种通孔