博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
javaagent入门指南
阅读量:3935 次
发布时间:2019-05-23

本文共 6762 字,大约阅读时间需要 22 分钟。

第一次见到javaagent时,是偶然了解到Spring的AOP中使用了一个Instrumentation技术,对自己来说是一个新的知识点,所以很好奇,因此查阅相关文档和资料进行学习,在此记录,如有不妥之处,请指正。

运行环境:

  • 操作系统:Windows10
  • jdk版本:openjdk version 11.0.7

概述

javaagent顾名思义就是一个java代理,我们知道任何一项java应用的启动都需要有一个入口函数,加载从入口函数开始一直扩散到整个应用。类在jvm中的加载顺序是:加载——>验证——>准备——>解析——>初始化。在加载阶段,jvm需要读入类的二进制信息,如果我们有一项技术,可以在验证阶段开始前对类的二进制信息进行操作,岂不是美滋滋?此时就需要用到java代理,从外部视角可以将它看做一个java程序的代理,可以通过它在类加载过程中对类信息进行合法操作。

当我们在启动应用程序时,可以使用-javaagent选项指定对应的javaagent:

-javaagent:
[=
<选项>
] 加载 Java 编程语言代理, 请参阅 java.lang.instrument

实际上instrument模块是在java.instrument下,不知道为什么它显示的不对:1592704787342

对于javaagent类,它的主函数前置启动方法(即main函数开始前的方法)的方法签名有特定的要求,必须要满足以下两种格式:

public static void premain(String agentArgs)public static void premain(String agentArgs, Instrumentation inst)

注意:文章的主函数前置启动方法主函数后置启方法都是我自己为了便于称呼起的。

jvm会优先加载第一种,如果第一种没有则加载第二种方法,反之则忽略:1592704979168

Instrumentation接口的方法如下:

public interface Instrumentation {
/** * 注册一个Class文件转换器,转换器可以观测到除转换器所用到的Class外的所有Class的定义。 * 转换器的顺序由ClassFileTransformer传入顺序决定,当转换器出现异常时,也会按照顺序 * 调用下一个转换器。 * 参数 canRetransform 设置是否允许重新转换。 */ void addTransformer(ClassFileTransformer transformer, boolean canRetransform); /** * Registers the supplied transformer. *

* Same as addTransformer(transformer, false). * * @param transformer the transformer to register * @throws java.lang.NullPointerException if passed a null transformer * @see #addTransformer(ClassFileTransformer,boolean) */ void addTransformer(ClassFileTransformer transformer); /** * 删除一个Class转换器 */ boolean removeTransformer(ClassFileTransformer transformer); /** * 判断JVM配置是否支持转换这个类 */ boolean isRetransformClassesSupported(); /** * 在类加载之后,重新定义该Class */ void retransformClasses(Class

... classes) throws UnmodifiableClassException; /** * 判断JVM配置是否支持重新定义这个类 */ boolean isRedefineClassesSupported(); /** * 使用提供的类文件重新定义提供的类集 */ void redefineClasses(ClassDefinition... definitions) throws ClassNotFoundException, UnmodifiableClassException; /** * 判断该Class是否被retransformation或redefinition修饰 */ boolean isModifiableClass(Class
theClass); /** * 返回已经被JVM加载的Class */ @SuppressWarnings("rawtypes") Class[] getAllLoadedClasses(); /** * 返回被初始化的所有类的集合 */ @SuppressWarnings("rawtypes") Class[] getInitiatedClasses(ClassLoader loader); /** * 返回指定对象的大小 */ long getObjectSize(Object objectToSize); /** * 添加一个包含由引导类加载器定义的instrumentation类的JAR文件。 */ void appendToBootstrapClassLoaderSearch(JarFile jarfile); /** * 添加一个包含由系统类加载器定义的instrumentation类的JAR文件。 */ void appendToSystemClassLoaderSearch(JarFile jarfile); /** * 判断当前JVM配置是否支持设置本地方法前缀 */ boolean isNativeMethodPrefixSupported(); /** * 设置本地方法浅醉 */ void setNativeMethodPrefix(ClassFileTransformer transformer, String prefix); /** * 重定义Module */ void redefineModule(Module module, Set
extraReads, Map
> extraExports, Map
> extraOpens, Set
> extraUses, Map
, List
>> extraProvides); /** * 判断Module是否重定义过 */ boolean isModifiableModule(Module module);}

对于javaagent的主函数后置启动方法(main函数执行后)的方法签名有以下两种:

public static void agentmain (String agentArgs, Instrumentation inst)public static void agentmain (String agentArgs)

同主函数前置启动方法一样,不带Instrumentation参数的方法优先级较高。对于主函数后置启动方法,它在加载时通过java的Attach API实现:1592715492945

我们要注意一下VirtualMachineVirtualMachineDescriptor类:

  • VirtualMachine映射的是一个java虚拟机,它提供了java虚拟机的相关信息访问和操作。它里面有个attache(String id)方法,用于通过pid来连接对应的目标虚拟机。它的loadAgent(String agent)方法可以给虚拟机注册一个agent,在这个agent中会得到一个Instrumentation实例,该实例可以在class加载前改变class字节码,也可以在 class加载后改变其字节码。
  • VirtualMachineDescriptor是一个描述虚拟机的容器类,其中的displayName方法和id方法用于返回虚拟机的名字和pid。
  • VirtualMachineImplVirtualMachine的进一步实现,它继承了HotSpotVirtualMachine类(不同的虚拟机的类不同,我的是HotSpot),而HotSpotVirtualMachine类又继承了VirtualMachine类。

注意:对于主函数后置启动方法的绑定,需要在MASIFEST.MF中添加如下信息:

# Agent-Class的值是实现了agentmain方法的全限定类名Agent-Class: bigkai.javaagent.AgentTest

示例

测试的目录结构为:

|--src     |--main          |--java               |--AgentTest.java               |--MainTest.java     |--resources          |--META-INF               |--MANIFEST.MF

注意:当你使用maven进行打包时,默认情况下maven会自动生成MANIFEST.MF,从而将你的文件覆盖,所以你需要对pom.xml文件进行一些配置:

org.apache.maven.plugins
maven-jar-plugin
2.4
true
bigkai.javaagent.AgentTest
bigkai.javaagent.AgentTest
true
true

premain方法

public class AgentTest {
/** * 将在主程序的main方法之前运行 * @param agentArgs 传递过来的参数 * @param inst agent技术主要使用的API可以使用它来改变和重新定义类的行为 */ public static void premain(String agentArgs, Instrumentation inst){
System.out.println("premain start"); System.out.println(agentArgs); }}
public class MainTest {
public static void main(String[] args) throws Exception {
System.out.println("main starting"); }}

修改MANIFEST.MF文件:

Manifest-Version: 1.0Can-Redefine-Classes: trueCan-Retransform-Classes: truePremain-Class: AgentTest

当创建了上面两个类后,便对文件进行打包,然后根据上面所说,对JVM参数进行设置:

-javaagent:E:\test_demo-1.0-SNAPSHOT.jar

接下来你可以启动你的MainTest程序,结果打印出来后是这样的:1592716617243

agentmain方法

public class AgentTest {
/** * 将在主程序的main方法之后运行 * @param agentArgs 传递过来的参数 * @param inst agent技术主要使用的API可以使用它来改变和重新定义类的行为 */ public static void agentmain(String agentArgs, Instrumentation inst){
System.out.println("agentmain start"); System.out.println(agentArgs); }}
public class MainTest {
public static void main(String[] args) throws Exception {
System.out.println("main starting"); List
list = VirtualMachine.list(); for (VirtualMachineDescriptor vmd : list){
if (vmd.displayName().equals("MainTest")){
VirtualMachine virtualMachine = VirtualMachine.attach(vmd.id()); virtualMachine.loadAgent("E:\\test_demo-1.0-SNAPSHOT.jar"); virtualMachine.detach(); } } }}

修改MANIFEST.MF文件:

Manifest-Version: 1.0Can-Redefine-Classes: trueCan-Retransform-Classes: truePremain-Class: AgentTestAgent-Class: AgentTest

当创建了上面两个类后,便对文件进行打包,然后根据上面所说,对JVM参数进行设置:

-javaagent:E:\test_demo-1.0-SNAPSHOT.jar -Djdk.attach.allowAttachSelf=true

接下来你可以启动你的MainTest程序,结果打印出来后是这样的:1592716686686

转载地址:http://rcqgn.baihongyu.com/

你可能感兴趣的文章
彻底解决 LINK : fatal error LNK1123: 转换到 COFF 期间失败: 文件无效或损坏
查看>>
GL中如何让画的点为圆形
查看>>
普通指针到智能指针的转换
查看>>
QT 的信号与槽机制介绍
查看>>
Meshless Deformations Based on Shape Matching
查看>>
install glm library in ubuntu and use it in qt
查看>>
As-Rigid-As-Possible Surface Modeling
查看>>
INSTALL CGAL on ubuntu and use it in qt
查看>>
cmake cannot find eigen3 in ubuntu
查看>>
how to add external library in qt under ubuntu
查看>>
how to get current path in qt
查看>>
How to resize disk partitions in ubuntu
查看>>
WIN7 VS2010下配置 CGAL-4.7
查看>>
how to install qt on ubuntu
查看>>
向量场的方向导数仍为向量场
查看>>
how to install window exe program on ubuntu
查看>>
how to run an android emulator in ubuntu 14.04
查看>>
change backgroud color of the windows in ubuntu
查看>>
change qtcreator3.6.0 backbround color on ubuntu 14.04
查看>>
igllib 202_gaussian curvature
查看>>