Agent 为 JVMTI 的客户端。

这里记录的是基于Java Instrument 的 Agent 实现,还有直接基于 JVMTI 的 Agent 实现

在 JDK1.5 以后,我们可以使用 Agent 技术构建一个独立于应用程序的代理程序,用来协助监测、运行甚至替换其他 JVM 上的程序。使用它可以实现虚拟机级别的 AOP 功能。

Agent 分为两种,一种是在主程序之前运行的 Agent,一种是在主程序之后运行的 Agent(JDK1.6 以后)。

一、在主程序运行之前的代理程序

1.编写 agent 程序

package before;

import java.lang.instrument.Instrumentation;

public class AgentApplication {
public static void premain(String arg, Instrumentation instrumentation) {
System.err.println("agent startup , args is " + arg);
}
}

2.添加 MANIFEST.MF 文件

路径为 META-INF/MANIFEST.MF

Manifest-Version: 1.0
Premain-Class: before.AgentApplication
Can-Redefine-Classes: true
Can-Retransform-Classes: true

若使用的是 Maven 编译就不用手动添加,配置 pom.xml 即可

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Premain-Class>before.AgentApplication</Premain-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>

3.运行

新建一个测试类并编译为 class 文件

package com;

public class Main {
public static void main(String[] args) {
System.out.println("123");
}
}

使用命令运行

java -javaagent:..\mahout-1.0-SNAPSHOT.jar=abc com.Main

可以看到 premain 方法在 main 之前运行了

二、在主程序运行之后的代理程序

关于动态 attach:https://openjdk.java.net/groups/hotspot/docs/Serviceability.html#battach

https://juejin.im/post/5b0d020d518825153f10403f

1.编写 agent 程序

由于是在主程序运行后再执行,意味着我们可以获取主程序运行时的信息,这里我们打印出来主程序中加载的类名。

package after;

import java.lang.instrument.Instrumentation;

public class AgentApplication {
public static void agentmain(String arg, Instrumentation instrumentation) {
System.err.println("agent startup , args is " + arg); Class<?>[] classes = instrumentation.getAllLoadedClasses();
for (Class<?> cls : classes) {
System.out.println(cls.getName());
}
}
}

2.添加 MANIFEST.MF 文件

路径为 META-INF/MANIFEST.MF

Manifest-Version: 1.0
Agent-Class: after.AgentApplication
Can-Redefine-Classes: true
Can-Retransform-Classes: true

若使用的是 Maven 编译就不用手动添加,配置 pom.xml 即可

<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifest>
<addClasspath>true</addClasspath>
</manifest>
<manifestEntries>
<Agent-Class>after.AgentApplication</Agent-Class>
<Can-Redefine-Classes>true</Can-Redefine-Classes>
<Can-Retransform-Classes>true</Can-Retransform-Classes>
</manifestEntries>
</archive>
</configuration>
</plugin>
</plugins>
</build>

3.运行

由于是在程序运行之后运行,需要先有一个 Java 进程,直接用 IDE 运行即可,不用使用命令行。

package com;

public class Main {
public static void main(String[] args) throws InterruptedException {
for (; ; ) {
System.out.println("123");
Thread.sleep(1000);
}
}
}

运行后查看改程序的进程号,这里为 9084

然后将 agent 程序附加到上面程序的进程中

com.sun.tools.attach.VirtualMachine 在 JAVA_HOME 路径下 lib/tools.jar 中,如果 IDE 报找不到,可以手动将 tools.jar 添加进来,或配置 CLASSPATH 环境变量。

package com;

import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine; import java.io.IOException; public class Attach {
public static void main(String[] args) throws IOException, AttachNotSupportedException, AgentLoadException, AgentInitializationException {
VirtualMachine vm = VirtualMachine.attach("9084");
vm.loadAgent("D:\\IDEA\\CodeLib\\jhxxb\\mahout\\target\\mahout-1.0-SNAPSHOT.jar");
}
}

直接 IDE 运行即可,执行完毕后回到 Main 程序的打印控制台

三、使用相关

其中 -javaagent 可以有多个,但如果把 -javaagent 放在 -jar 后面,则不会生效。也就是放在主程序后面的 agent 是无效的。

如:java -javaagent:D:\myagent-1.jar=ABC -javaagent:D:\myagent-2.jar=DEF -jar myapp.jar -javaagent:D:\myagent-3.jar=GHI,其中 myagent-3.jar 是无效的。

其中 premain(agentmain) 方法有两个:

  1. public static void premain(String agentArgs, Instrumentation inst)
  2. public static void premain(String agentArgs)

JVM 会优先加载 1,加载成功则忽略 2,如没有 1,则加载 2。加载逻辑在 sun.instrument.InstrumentationImpl 类中:

private void loadClassAndStartAgent(String classname, String methodname, String optionsString) throws Throwable {
ClassLoader mainAppLoader = ClassLoader.getSystemClassLoader();
Class<?> javaAgentClass = mainAppLoader.loadClass(classname); Method m = null;
NoSuchMethodException firstExc = null;
boolean twoArgAgent = false; // The agent class must have a premain or agentmain method that
// has 1 or 2 arguments. We check in the following order:
//
// 1) declared with a signature of (String, Instrumentation)
// 2) declared with a signature of (String)
// 3) inherited with a signature of (String, Instrumentation)
// 4) inherited with a signature of (String)
//
// So the declared version of either 1-arg or 2-arg always takes
// primary precedence over an inherited version. After that, the
// 2-arg version takes precedence over the 1-arg version.
//
// If no method is found then we throw the NoSuchMethodException
// from the first attempt so that the exception text indicates
// the lookup failed for the 2-arg method (same as JDK5.0). try {
m = javaAgentClass.getDeclaredMethod(methodname, new Class<?>[]{String.class, java.lang.instrument.Instrumentation.class});
twoArgAgent = true;
} catch (NoSuchMethodException x) {
// remember the NoSuchMethodException
firstExc = x;
} if (m == null) {
// now try the declared 1-arg method
try {
m = javaAgentClass.getDeclaredMethod(methodname, new Class<?>[]{String.class});
} catch (NoSuchMethodException x) {
// ignore this exception because we'll try
// two arg inheritance next
}
} if (m == null) {
// now try the inherited 2-arg method
try {
m = javaAgentClass.getMethod(methodname, new Class<?>[]{String.class, java.lang.instrument.Instrumentation.class});
twoArgAgent = true;
} catch (NoSuchMethodException x) {
// ignore this exception because we'll try
// one arg inheritance next
}
} if (m == null) {
// finally try the inherited 1-arg method
try {
m = javaAgentClass.getMethod(methodname, new Class<?>[]{String.class});
} catch (NoSuchMethodException x) {
// none of the methods exists so we throw the
// first NoSuchMethodException as per 5.0
throw firstExc;
}
} // the premain method should not be required to be public,
// make it accessible so we can call it
// Note: The spec says the following:
// The agent class must implement a public static premain method...
setAccessible(m, true); // invoke the 1 or 2-arg method
if (twoArgAgent) {
m.invoke(null, new Object[]{optionsString, this});
} else {
m.invoke(null, new Object[]{optionsString});
} // don't let others access a non-public premain method
setAccessible(m, false);
}

Instrument premain、agentmain 方法执行时机:

premain 执行时机:在 JVM 启动时(所有的 Java 类都未被初始化,所有的对象实例都未被创建),初始化函数 eventHandlerVMinit 会调用 sun.instrument.instrumentationImpl 类的 loadClassAndCallPremain 方法去执行 Premain-Class 指定类的 premain 方法。

agentmain 执行时机:在 JVM 启动后,通过 VirtualMachine 附着一个 Instrument,如:vm.loadAgent(jar),会调用 sun.instrument.instrumentationImpl 类的 loadClassAndCallAgentmain 方法去执行 Agentmain-Class 指定类的 agentmain 方法。

premain、agentmain 方法中两个参数:

agentArgs:代理程序命令行中输入参数,随同 “-javaagent” 一起传入,与 main 函数不同的是,这个参数是一个字符串而不是一个字符串数组。

inst:java.lang.instrument.Instrumentation 实例,由 JVM 自动传入,集中了几乎所有功能方法,如:类操作、classpath 操作等。

META-INF/MAINFEST.MF 参数:

  • Premain-Class:指定包含 premain 方法的类名。
  • Agent-Class:指定包含 agentmain 方法的类名。
  • Boot-Class-Path:指定引导类加载器搜索的路径列表。查找类的特点于平台的机制失败后,引导类加载器会搜索这些路径。
  • Can-Redefine-Class:是否能重新定义此代理所需的类,默认为 false。
  • Can-Retransform-Class:是否能重新转换此代理所需的类,默认为 false。
  • Can-Set-Native-Method-Prefix:是否能设置此代理所需的本机方法前缀,默认值为 false。

https://www.jianshu.com/p/63c328ca208d

https://yq.aliyun.com/articles/658806

https://www.jianshu.com/p/9f4e8dcb3e2f

https://juejin.im/post/5b0925ec51882538aa1ee248

https://www.ibm.com/developerworks/cn/java/j-lo-jpda2

Java-基于 Instrument 的 Agent的更多相关文章

  1. 基于Java Instrument的Agent实现

    使用 Instrumentation,使得开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义.有了这样的功能,开发者就可以 ...

  2. java.lang.instrument 中的premain 实现类的个性化加载(附源代码)

    背景 想调用ASM API (用于字节码处理的开源API)对字节码进行处理,目标是实现对java程序运行时各种对象的动态跟踪,并进一步分析各个对象之间的关系(研究前提是目前的UML锁阐释的whole- ...

  3. java.lang.instrument.Instrumentation

    java.lang.instrument.Instrumentation 看完文档之后,我们发现这么两个接口:redefineClasses和retransformClasses.一个是重新定义cla ...

  4. java.lang.instrument使用

    Java在1.5引入java.lang.instrument,你可以由此实现一个Javaagent,通过此agent来修改类的字节码即改变一个类. 程序启动之时启动代理(pre-main) 通过jav ...

  5. Golang、Php、Python、Java基于Thrift0.9.1实现跨语言调用

    目录: 一.什么是Thrift? 1) Thrift内部框架一瞥 2) 支持的数据传输格式.数据传输方式和服务模型 3) Thrift IDL 二.Thrift的官方网站在哪里? 三.在哪里下载?需要 ...

  6. J2SE 1.6 特性:java.lang.instrument

    1. import java.lang.instrument.Instrumentation; public class ObjectSizeFetcher { private static Inst ...

  7. Java基于opencv实现图像数字识别(五)—投影法分割字符

    Java基于opencv实现图像数字识别(五)-投影法分割字符 水平投影法 1.水平投影法就是先用一个数组统计出图像每行黑色像素点的个数(二值化的图像): 2.选出一个最优的阀值,根据比这个阀值大或小 ...

  8. Java基于opencv实现图像数字识别(四)—图像降噪

    Java基于opencv实现图像数字识别(四)-图像降噪 我们每一步的工作都是基于前一步的,我们先把我们前面的几个函数封装成一个工具类,以后我们所有的函数都基于这个工具类 这个工具类呢,就一个成员变量 ...

  9. Java基于opencv实现图像数字识别(三)—灰度化和二值化

    Java基于opencv实现图像数字识别(三)-灰度化和二值化 一.灰度化 灰度化:在RGB模型中,如果R=G=B时,则彩色表示灰度颜色,其中R=G=B的值叫灰度值:因此,灰度图像每个像素点只需一个字 ...

随机推荐

  1. CSS3实现瀑布流布局

    讲干货,不啰嗦,瀑布流布局是种常见的布局方式,常用于图片相关的样式展示,通过CSS3的多列(Multi-column)属性,可以简单的实现类似效果. 具体步骤: 1.设置外部容器多列列数(column ...

  2. TCP三次握手过程中涉及的队列知识的学习

    先上一张图 (图片来源:http://www.cnxct.com/something-about-phpfpm-s-backlog/) 如上图所示,这里有两个队列:syns queue(半连接队列): ...

  3. centos安装netcat工具及测试

    netcat是网络工具中的瑞士军刀,它能通过TCP和UDP在网络中读写数据.通过与其他工具结合和重定向,你可以在脚本中以多种方式使用它.使用netcat命令所能完成的事情令人惊讶. netcat所做的 ...

  4. Image Processing and Analysis_8_Edge Detection:Learning to Detect Natural Image Boundaries Using Local Brightness, Color, and Texture Cues ——2004

    此主要讨论图像处理与分析.虽然计算机视觉部分的有些内容比如特 征提取等也可以归结到图像分析中来,但鉴于它们与计算机视觉的紧密联系,以 及它们的出处,没有把它们纳入到图像处理与分析中来.同样,这里面也有 ...

  5. depth/stencil buffer的作用 ----------理解模板缓存 opengl

    在D3D11中,有depth/stencil buffer,它们和framebuffer相对应,如下图所示,framebuffer中一个像素,有相对应的depth buffer和stencil buf ...

  6. Wireless Network(并查集)

    POJ - 2236 #include<iostream> #include<algorithm> #include<cstring> #include<cm ...

  7. PHP开发中常用的字符串操作函数

    1,拼接字符串 拼接字符串是最常用到的字符串操作之一,在PHP中支持三种方式对字符串进行拼接操作,分别是圆点.分隔符{}操作,还有圆点等号.=来进行操作,圆点等号可以把一个比较长的字符串分解为几行进行 ...

  8. [ 转载 ]hashCode方法的相关用法

    想要明白hashCode的作用,你必须要先知道Java中的集合. 总的来说,Java中的集合(Collection)有两类,一类是List,再有一类是Set. 你知道它们的区别吗?前者集合内的元素是有 ...

  9. 基于SSM框架的通用权限框架设计

     1. 整体解决方案概述    1.1 权限整体解决方案概述     权限设计主要有一下几大部分组成:     PassPort:    针对现在系统的分析,系统之间有部分信息是共享的,这部分信息将由 ...

  10. python的变量命名规范

    一.python变量名命名规则: 1.变量名通常由字母,数字,下划线组成; 2.数字不能作为变量名开头; 3.不能以python中的关键字命名; 4.变量名要有意义; 5.不要用汉字和拼音去命名; 6 ...