`
923080512
  • 浏览: 190811 次
  • 性别: Icon_minigender_1
  • 来自: 商丘
社区版块
存档分类
最新评论

Java Attach API

    博客分类:
  • java
 
阅读更多

 

Java Attach API

 

catalog

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

 

1. instrument与Attach API

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

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

但是在实际的很多的情况下,我们没有办法在虚拟机启动之时就为其设定代理,这样实际上限制了instrument的应用。而Java SE 6的新特性改变了这种情况,通过Java Tool API中的attach方式,我们可以很方便地在运行过程中动态地设置加载代理类,以达到instrumentation的目的
​在JDK6中,针对这点做了改进,开发者可以在main开始执行以后,再开启自己的Instrument程序
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.java

2. 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.java

2. 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");
    }
    //先连接上目标JVM
    hProcess = 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 name
    int 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 pipe
        PipedInputStream is = new PipedInputStream(hPipe);

        // read completion status
        int status = readInt(is);
        if (status != 0) 
        {
        // special case the load command so that the right exception is thrown
        if (cmd.equals("load")) 
        {
            throw new AgentLoadException("Failed to load agent library");
        } 
        else 
        {
            throw new IOException("Command failed in target VM");
        }
        }

        // return the input stream
        return 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

分享到:
评论

相关推荐

    JavaProbe:Java运行时信息收集工具,该工具使用Java Attach API进行信息获取

    关于JavaProbe: 一个Java运行时信息收集工具,该工具使用Java Attach API进行信息获取(相关的技术文章链接附在本文的结尾)。指示: 环境: 1. jdk version &gt;= 1.6, linux, runuser (used to solve privilege ...

    jdk14-API开发文档.zip

    为了推动使用更安全的Java Attach API,JDK 14开始弃用某些内置的Attach API。 ### API变更与改进 - 类库中的许多类和方法都得到了更新,以支持新的语言特性,例如`Objects.requireNonNullElse()`用于提供默认值...

    javaAgent实现补丁不重出功能,通过attach实现程序运行时加载,同时通过shell脚本部署到容器内

    Java Attach API允许我们在运行的Java虚拟机上附加和管理其他工具,如JavaAgent。通过调用`VirtualMachine.attach(String pid)`方法,我们可以指定目标进程的ID并附加JavaAgent。一旦附加成功,就可以调用`loadAgent...

    jdk-11.0.10 src.zip

    5. **jdk.attach**:这是Java Attach API的源代码,允许在运行时附加管理工具到Java进程。 6. **jdk.internal.vm.compiler.management**:这部分可能涉及到JVM编译器(如JIT)的管理和监控,包括性能优化的实现。 7....

    jdk11的源码src文件

    5. **jdk.attach**: 这个模块提供了Java Attach API,允许在运行时附加到本地Java虚拟机(JVM)进行诊断或管理操作。这对于故障排查和性能分析非常有用。 6. **jdk.internal.vm.compiler.management**: 这部分源码...

    jdk源码包jdk-11.0.1

    6. **jdk.attach**:这部分源码与Java Attach API相关,允许本地应用或者管理工具附加到正在运行的Java虚拟机上,进行诊断、监控或者管理操作。 7. **jdk.internal.vm.compiler.management**:这部分源码涉及到JVM...

    JAVA-mail-API.zip_Attachments

    Java Mail API 是一个用于处理电子邮件的开源库,它允许开发者在Java应用程序中发送、接收和管理邮件。在“JAVA-mail-API.zip_Attachments”这个压缩包中,主要的知识点是利用Java Mail API来发送带有附件的邮件。...

    使用JAVA MAIL API 的实例

    JavaMail API是Java平台上用于处理电子邮件的标准库,它允许开发者在应用程序中添加发送、接收和管理电子邮件的功能。在这个实例中,我们将深入探讨如何使用JavaMail API进行邮件收发以及添加附件。 首先,确保你的...

    利用Java并配置Foxmail发送邮箱(携带附件)

    首先,我们需要理解Java Mail API,它是Java中用于处理邮件的核心库。 Java Mail API提供了处理邮件的所有必要工具,包括创建、读取、发送和管理邮件。为了使用它,我们需要在项目中引入相关的依赖。通常,我们使用...

    Java邮件开发Fundamentals of the JavaMail API

    Fundamentals of the JavaMail API Presented by developerWorks, your source for great tutorials ibm.com/developerWorks Table of Contents If you're viewing this document online, you can click ...

    java发邮件 带附件 多媒体邮件

    本示例代码及说明适用于希望利用Java Mail API发送包含丰富格式内容邮件的开发者。 ### Java 发送带有附件及多媒体内容的邮件 #### 一、Java Mail API 简介 Java Mail API 是一个强大的工具包,用于发送、接收和...

    tt2016_attach_api_agent_loader

    先决条件系统变量JAVA_HOME设置为JDK目录执行发现正在运行的虚拟机的进程ID: jps输出示例: $ jps101607832 jar3356 Jps加载Java代理: java -classpath $JAVA_HOME/lib/tools.jar:attach-api-agent-loader.jar -D...

    java Exchange收发邮件加附件

    Java通过Exchange进行邮件收发及附件操作主要涉及的是Microsoft Exchange Web Services (EWS) API的使用,这是一种基于SOAP协议的服务,允许开发者通过编程方式与Exchange Server进行交互。下面将详细介绍如何利用...

    java邮件开发jar包级帮助文档

    Java Mail API是Java平台中处理电子邮件的标准API,它提供了一系列接口和类,用于发送和接收邮件。本帮助文档将深入讲解如何使用Java Mail API进行邮件开发,以及涉及到的相关jar包。 首先,Java Mail API的核心jar...

    Java中发送Mail

    Java Mail API 是一个用于处理邮件的开源库,它允许开发者在Java应用程序中创建、发送和接收电子邮件。本教程将深入讲解如何使用Java Mail API来发送邮件。 首先,你需要在项目中引入Java Mail API的相关依赖。在...

    Java邮件发送.zip

    首先,Java中用于发送邮件的主要API是JavaMail API,它是一个开源库,提供了一组接口和类,允许开发者通过SMTP(简单邮件传输协议)等协议发送邮件。JavaMail API的核心包有`javax.mail`和`javax.mail.internet`,...

    java mail 发送QQ邮件

    Java Mail 是一个用于处理电子邮件的开源API,它允许开发者通过编程方式发送、接收和管理邮件。在Java中,使用Java Mail API发送QQ邮件是常见的需求,尤其对于自动化通知或者服务端消息传递非常实用。以下是对这个...

    JAVA JDK完整源代码

    1. **JDK.Attach**: 这个模块提供了与Java虚拟机(JVM)进行交互的接口,比如附加本地程序到运行中的JVM上,或者执行JVM上的管理命令。这对于远程诊断和性能分析非常有用。 2. **Java.activation**: Java ...

    基于Java的邮件发送模板

    首先,JavaMail API是Java中用于处理电子邮件的主要库。它提供了丰富的API,可以用来创建、发送和读取邮件。要使用JavaMail,我们需要引入相关的依赖。如果你使用的是Maven项目,可以在pom.xml文件中添加以下依赖: ...

    java 发邮件带excel附件,以流的形式发送附件,不生成excel文件

    在Java编程中,发送带有Excel附件的电子邮件是一项常见的任务,特别是在数据报告或自动化流程中。在某些场景下,我们可能希望直接以流的形式处理Excel数据,而不是先将其保存为一个实际的文件,然后再作为附件发送。...

Global site tag (gtag.js) - Google Analytics