Java - agent探针

Java - agent探针

介绍

使用 Instrumentation,使得开发者可以构建一个独立于应用程序的代理程序(Agent),用来监测和协助运行在 JVM 上的程序,甚至能够替换和修改某些类的定义。有了这样的功能,开发者就可以实现更为灵活的运行时虚拟机监控和 Java 类操作了,这样的特性实际上提供了 一种虚拟机级别支持的 AOP 实现方式,使得开发者无需对 JDK 做任何升级和改动,就可以实现某些 AOP 的功能了。

JVM启动前静态Instrument

Instrumentation 的最大作用,就是类定义动态改变和操作。在 Java SE 5 及其后续版本当中,开发者可以在一个普通 Java 程序(带有 main 函数的 Java 类)运行时,通过 -javaagent参数指定一个特定的 jar 文件(包含 Instrumentation 代理)来启动 Instrumentation 的代理程序。

编写premain方法

  1. 定义一个java类,实现如下两个方法当中的一个:

    public static void premain(String agentArgs, Instrumentation inst);  [1]
    public static void premain(String agentArgs); [2]
    

    其中,[1] 的优先级比 [2] 高,将会被优先执行([1] 和 [2] 同时存在时,[2] 被忽略)。在这个 premain 函数中,开发者可以进行对类的各种操作。

    • agentArgs 是 premain 函数得到的程序参数,随同 “-javaagent”一起传入。与 main 函数不同的是,这个参数是一个字符串而不是一个字符串数组,如果程序参数有多个,程序将自行解析这个字符串。
    • Inst 是一个 java.lang.instrument.Instrumentation 的实例,由 JVM 自动传入。java.lang.instrument.Instrumentation 是 instrument 包中定义的一个接口,也是这个包的核心部分,集中了其中几乎所有的功能方法,例如类定义的转换和操作等等。
  2. 打包jar
    将这个java类打包成一个jar文件,并在其中的META-INF/MAINFEST.MF属性当中加入”Premain-Class“来指定步骤1中编写的带有premain的类。maven打包方式可以参考配置如下

     <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-jar-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <!--<archive>-->
                        <!--<index>true</index>-->
                        <!--<manifestFile>-->
                            <!--src/main/resources/META-INF/MANIFEST.MF-->
                        <!--</manifestFile>-->
                        <!--<manifest>-->
                            <!--<addDefaultImplementationEntries/>-->
                        <!--</manifest>-->
                    <!--</archive>-->
                   <archive>
                    <manifest>
                        <addClasspath>true</addClasspath>
                    </manifest>
                    <manifestEntries>
                        <!-- 参数方式启动agent需要这个 -->
                        <Premain-Class>
                            ${permain}
                        </Premain-Class>
                        <!-- 启动后附加启动agent需要这个 -->
                        <Agent-Class>
                            com.demo.XXX
                        </Agent-Class>
                        <!-- 是否可以重新转换类 -->
                        <Can-Retransform-Classes>
                            true
                        </Can-Retransform-Classes>
    
                        <!-- 是否可以重新定义类 -->
                        <Can-Redefine-Classes>
                            true
                        </Can-Redefine-Classes>
    
                    </manifestEntries>
                </archive>
                </configuration>
            </plugin>
    
  3. 运行
    用如下方式运行带有Instrumenttation的java程序

    java -javaagent:jar路径 [= 传入 premain 的参数 ] 其他参数
    

JVM启动后动态Instrument

在java6之前,开发者只能在程序main函数执行前,启动Instrumentation,java6之后提供了一个与premain类似的agentmain方法,该方法可以在程序main函数运行之后在执行。写法与premain函数类似。

public static void agentmain (String agentArgs, Instrumentation inst); [1] 
public static void agentmain (String agentArgs);[2]

同样,[1] 的优先级比 [2] 高,将会被优先执行([1] 和 [2] 同时存在时,[2] 被忽略)。跟 premain 函数一样,开发者可以在 agentmain 中进行对类的各种操作。其中的 agentArgs 和 Inst 的用法跟 premain 相同。
打包方法与premain类类似,唯一区别是MANIFEST文件的Agent-Class替换Premain-Class。
启动方式与premain类也不相同,agentmain实现类可以通过Attach API来触发。

 String pid = "34534";// 目标JVM进程ID
 VirtualMachine virtualMachine = VirtualMachine.attach(pid);
 virtualMachine.loadAgent(jar);
 virtualMachine.detach();
 System.out.println("load success");

Instrument核心API

  • ClassFileTransformer:定义了类加载前的预处理类,可以在这个类中对要加载的类的字节码做一些处理,譬如进行字节码增强;
  • Instrumentation:增强器,由JVM在入口参数中传递给我们,提供了如下的功能:
    1. addTransformer/removeTransformer:注册/删除ClassFileTransformer;
    2. retransformClasses:对于已经加载的类重新进行转换处理,即会触发重新加载类定义,需要注意的是,新加载的类不能修改旧有的类声明,譬如不能增加属性、不能修改方法声明;
    3. redefineClasses:与如上类似,但不是重新进行转换处理,而是直接把处理结果(bytecode)直接给JVM;
    4. getAllLoadedClasses:获得当前已经加载的Class,可配合retransformClasses使用;
    5. getInitiatedClasses:获得由某个特定的ClassLoader加载的类定义;
    6. getObjectSize:获得一个对象占用的空间,包括其引用的对象;
    7. appendToBootstrapClassLoaderSearch/appendToSystemClassLoaderSearch:增加BootstrapClassLoader/SystemClassLoader的搜索路径;
    8. isNativeMethodPrefixSupported/setNativeMethodPrefix:判断JVM是否支持拦截Native Method;

热部署,其实就是动态或者说运行时修改类,大的方向说有2种方式:

  • 使用 agentmain,不需要重新创建类加载器,可直接修改类,但是有很多限制。
  • 使用 premain 可以在类第一次加载之前修改,加载之后修改需要重新创建类加载器。或者 在自定义的类加载器种修改,但这种方式比较耦合。
    无论是哪种,都需要字节码修改的库,比如ASM,javassist ,cglib 等,很多。总之,通过java.lang.instrument 包配合字节码库,可以很方便的动态修改类,或者进行热部署。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,951评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,606评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,601评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,478评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,565评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,587评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,590评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,337评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,785评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,096评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,273评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,935评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,578评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,199评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,440评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,163评论 2 366
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,133评论 2 352

推荐阅读更多精彩内容