技术经验分享:javaagent技术&Attach技术

简介: 技术经验分享:javaagent技术&Attach技术

  之前见过好多种-javaagent 参数,比如我们IDEA启动一个类的时候就会有好多的javaagent。 好像又叫探针技术,简单研究下其过程。


  Java 5 中提供的 Instrument 包启动时往 Java 虚拟机中挂上一个用户定义的 hook 程序,可以在装入特定类的时候改变特定类的字节码,从而改变该类的行为。Instrument 包是在整个虚拟机上挂了一个钩子程序,每次装入一个新类的时候,都必须执行一遍这段程序,即使这个类不需要改变。一个核心类是sun.instrument.InstrumentationImpl, 这个类可以动态的增加转换器或者获取当前JVM加载的所有的类信息。


  参考:


第一种: 使用 premain 可以在类第一次加载之前修改类信息,加载之后修改需要重新创建类加载器, premain是Java SE5开始就提供的代理方式。而且使用时必须在命令行指定代理jar,并且代理类必须在main方法前启动。


第二种: Java SE6开始,提供了在应用程序的VM启动后在动态添加代理的方式,即agentmain方式。


1. premain 使用


1. 简单使用


1. agent.MyAgent


package agent;


import java.lang.instrument.Instrumentation;


public class MyAgent {


/


JVM 在类加载前会调用到此函数



@param agentOps


@param inst


/


public static void premain(String agentOps, Instrumentation inst) {


System.out.println("agent.MyAgent.premain start ");


System.out.println(agentOps);


System.out.println(inst);


System.out.println("agent.MyAgent.premain end ");


}


}


2. 编写META-INF/MANIFEST.MF


Manifest-Version: 1.0


Can-Retransform-Classes: true


Premain-Class: agent.MyAgent


这个配置文件需要注意格式,如果之前打过jar 包应该会注意。 最后有个空行, 每个key后面的冒号 和 value 之间有个空格


3. 最后的目录结构如下:


$ ls -R


.:


agent/ META-INF/


./agent:


MyAgent.class


./META-INF:


MANIFEST.MF


4. 生成jar 包


D:\agentjar>jar cvfm agent.jar ./META-INF/MANIFEST.MF ./


已添加清单


正在添加: agent/(输入 = 0) (输出 = 0)(存储了 0%)


正在添加: agent/MyAgent.class(输入 = 754) (输出 = 415)(压缩了 44%)


正在忽略条目META-INF/


正在忽略条目META-INF/MANIFEST.MF


5. 新建测试类:


public class PlainTest {


public static void main(String【】 args) throws InterruptedException {


new PlainTest().test();


}


public void test() throws InterruptedException {


Thread.sleep(5 1000);


System.out.println("cn.qz.PlainTest.test\t" + 111222);


}


}


6. 编译运行测试:


$ java -javaagent:D:/agentjar/agent.jar PlainTest


agent.MyAgent.premain start


null


sun.instrument.InstrumentationImpl@5fe5c6f


agent.MyAgent.premain end


cn.qz.PlainTest.test 111222


测试传递参数: (可以传递参数给指定的方法, 方法内部也可以根据参数进行一些特殊的处理)


$ java -javaagent:D:/agentjar/agent.jar=key1=value1,key2=value2 PlainTest


agent.MyAgent.premain start


key1=value1,key2=value2


sun.instrument.InstrumentationImpl@5fe5c6f


agent.MyAgent.premain end


cn.qz.PlainTest.test 111222


7. 使用IDEA 的方式进行调试


  同样的jar 包指定使用之前打的jar,和探针对应的类可以在DIEA 中使用java 文件进行调试。


(1) 目录结构


(2) agent.MyAgent


package agent;


import java.lang.instrument.Instrumentation;


public class MyAgent {


/


JVM 在类加载前会调用到此函数



@param agentOps


@param inst


/


public static void premain(String agentOps, Instrumentation inst) {


System.out.println("agent.MyAgent.premain XXX start ");


System.out.println(agentOps);


System.out.println(inst);


System.out.println("agent.MyAgent.premain XXX end ");


}


}


(3) 增加测试类


package cn.qz;


public class PlainTest {


public static void main(String【】 args) throws InterruptedException {


new PlainTest().test();


}


public void test() throws InterruptedException {


Thread.sleep(5 1000);


System.out.println("cn.qz.PlainTest.test\t" + 111222);


}


}


(4) 编辑增加代理 idea 中 Add VM Operations 增加参数: -javaagent:D:\agentjar\agent.jar


(5) 测试查看运行结果:


agent.MyAgent.premain XXX start


null


sun.instrument.InstrumentationImpl@1eb44e46


agent.MyAgent.premain XXX end


cn.qz.PlainTest.test 111222


(6) debug 到agent.MyAgent#premain 方法内部, 查看调用链:


2. premain 实现监测方法执行时间的操作


  基于javassit 对字节码进行增强。


1. pom 增加


org.javassist


javassist


3.28.0-GA


2. agent.MyAgent 源码


package agent;


import javassist.ClassPool;


import javassist.CtClass;


import javassist.CtMethod;


import java.lang.instrument.ClassFileTransformer;


import java.lang.instrument.IllegalClassFormatException;


import java.lang.instrument.Instrumentation;


import java.security.ProtectionDomain;


public class MyAgent {


/


有该方法会优先执行该方法



@param agentOps


@param inst


*/


public static void premain(String agentOps, Instrumentation inst) {


System.out.println("====premain 方法执行");


System.out.println(agentOps);


System.out.println(inst);


System.out.println("====premain2 方法执行");


/


添加一个转换器, 字节码加载到虚拟机前会调用此类的transform 方法


/


inst.addTransformer(new ClassFileTransformer() {


@Override


public byte【】 transform(ClassLoader loader, String className, Class classBeingRedefined, ProtectionDomain protectionDomain, byte【】 classfileBuffer) throws IllegalClassFormatException {


if (!className.startsWith("cn/qz")) {


return null;


}


System.out.println("ClassLoader : " + loader);


System.out.println("className : " + className);


//创建类,这是一个单例对象


ClassPool cp = ClassPool.getDefault();


//我们需要构建的类


try {


CtClass ctClass = cp.get(className.replace("/", "."));


CtMethod【】 declaredMethods = ctClass.getDeclaredMethods();


for (CtMethod method : declaredMethods) {


// 修改方法体来实现, 增加两个局部变量用于记录执行时间


method.addLocalVariable("startTimeAgent", CtClass.longType);


method.insertBefore("startTimeAgent = System.currentTimeMillis();");


method.addLocalVariable("methodNameAgent", cp.get(String.class.getName()));


method.insertBefore("methodNameAgent = \""//代码效果参考:http://www.jhylw.com.cn/211138523.html

+ method.getLongName() + "\";");

method.insertAfter("System.out.println(methodNameAgent + \" exec time is :\" + (System.currentTimeMillis() - startTimeAgent) + \"ms\");");


}


return ctClass.toBytecode();


} catch (Exception e) {


e.printStackTrace();


}


return null;


}


});


}


}


3. 使用上面测试类进行测试查看日志


====premain 方法执行


null


sun.instrument.InstrumentationImpl@1eb44e46


====premain2 方法执行


ClassLoader : sun.misc.Launcher$AppClassLoader@18b4aac2


className : cn/qz/PlainTest


cn.qz.PlainTest.test 111222


cn.qz.PlainTest.test() exec time is :5001ms


cn.qz.PlainTest.main(java.lang.String【】) exec time is :5002ms


4. 打包:


(1) 下载pom 依赖的jar 包, 进入项目目录(有pom.xml的目录),cmd执行如下命令


mvn dependency:copy-dependencies -DoutputDirectory=dependency_lib


(2) 构造目录结构:


$ ls -R


.:


agent/ dependency_lib/ META-INF/


./agent:


'MyAgent$1.class' MyAgent.class


./dependency_lib:


javassist-3.28.0-GA.jar


./META-INF:


MANIFEST.MF


(3) 修改MANIFEST.MF


Manifest-Version: 1.0


Can-Retransform-Classes: true


Class-Path: dependency_lib/javassist-3.28.0-GA.jar


Premain-Class: agent.MyAgent


(4) 替换编译后的class 文件


(5) 打包


D:\agentjar>jar cvfm agent.jar ./META-INF/MANIFEST.MF ./


已添加清单


正在添加: agent/(输入 = 0) (输出 = 0)(存储了 0%)


正在添加: agent/MyAgent$1.class(输入 = 3142) (输出 = 1533)(压缩了 51%)


正在添加: agent/MyAgent.class(输入 = 941) (输出 = 524)(压缩了 44%)


正在添加: dependency_lib/(输入 = 0) (输出 = 0)(存储了 0%)


正在添加: dependency_lib/javassist-3.28.0-GA.jar(输入 = 851531) (输出 = 794719)(压缩了 6%)


正在忽略条目META-INF/


正在忽略条目META-INF/MANIFEST.MF


(6) 编写测试类编译后测试:


package cn.qz;


import java.util.concurrent.CountDownLatch;


public class PlainTest {


public static void main(String【】 args) throws InterruptedException {


new PlainTest().test();


CountDownLatch countDownLatch = new CountDownLatch(1);


countDownLatch.await();


}


public void test() throws InterruptedException {


Thread.sleep(5 1000);


System.out.println("cn.qz.PlainTest.test\t" + 111222);


}


}


1》测试:


D:\agentjar>java -javaagent:D:\agentjar\agent.jar cn.qz.PlainTest


====premain 方法执行


null


sun.instrument.InstrumentationImpl@6979e8cb


====premain2 方法执行


ClassLoader : jdk.internal.loader.ClassLoaders$AppClassLoader@383534aa


className : cn/qz/PlainTest


cn.qz.PlainTest.test 111222


cn.qz.PlainTest.test() exec time is :5001ms


2》 使用arthas 实时反编译查看生成的类信息


/


Decompiled with CFR.


/


package cn.qz;


import java.util.concurrent.CountDownLatch;


public class PlainTest {


/


WARNING - void declaration


/


public static void main(String【】 stringArray) throws InterruptedException {


void var2_1;


void var4_2;


long startTimeAgent = System.currentTimeMillis();


String methodNameAgent = "cn.qz.PlainTest.main(java.lang.String【】)";


new PlainTest().test();


CountDownLatch countDownLatch = new CountDownLatch(1);


/10/ countDownLatch.await();


Object var6_4 = null;


System.out.println(new StringBuffer().append((String)var4_2).append(" exec time is :").append(System.currentTimeMillis() - var2_1).append("ms").toString());


}


/


WARNING - void declaration


/


public void test() throws InterruptedException {


void var1_1;


void var3_2;


long startTimeAgent = System.currentTimeMillis();


String methodNameAgent = "cn.qz.PlainTest.test()";


/14/ Thread.sleep(5000L);


/15/ System.out.println("cn.qz.PlainTest.test\t111222");


Object var5_3 = null;


System.out.println(new StringBuffer().append((String)var3_2).append(" exec time is :").append(System.currentTimeMillis() - var1_1).append("ms").toString());


}


}</

相关文章
|
7天前
|
Arthas 监控 Java
拥抱 OpenTelemetry:阿里云 Java Agent 演进实践
本文介绍了阿里云 Java Agent 4.x 版本在基于 OTel Java Agent 二次开发过程中的实践与思考,并重点从功能、性能、稳定性、兼容性四个方面介绍了所做的工作。同时也介绍了阿里云可观测团队积极参与开源建设取得的丰厚成果。
拥抱 OpenTelemetry:阿里云 Java Agent 演进实践
|
2月前
|
Java 编译器 Maven
【颠覆你的认知!】当Quarkus邂逅GraalVM本机镜像,应用启动竟快到飞起——背后的技术秘密等你揭秘!
Quarkus框架因轻量级与高性能而在Java开发社区广受关注。结合GraalVM使用能显著提升应用启动速度与运行效率,这得益于GraalVM的本机镜像支持。本文将介绍如何利用Quarkus和GraalVM构建高效应用,并提供示例代码演示具体步骤。首先需安装GraalVM环境并配置Maven支持构建本机镜像。接着创建一个简单的RESTful服务端点作为示例,通过命令行编译生成本机可执行文件并运行。这种方式能够大幅提升应用性能,但需注意构建时间和部分Java特性兼容性问题。
46 1
|
6月前
|
分布式计算 大数据 Hadoop
【经验分享】用Linux脚本管理虚拟机下的大数据服务
【经验分享】用Linux脚本管理虚拟机下的大数据服务
59 1
|
Arthas 物联网 测试技术
《workshop专场--容器、消息&IoT专场-开发者动手实践营-容器、消息和IoT-Java诊断利器Arthas排查问题实践》电子版地址
workshop专场--容器、消息&IoT专场-开发者动手实践营-容器、消息和IoT-Java诊断利器Arthas排查问题实践
104 0
《workshop专场--容器、消息&IoT专场-开发者动手实践营-容器、消息和IoT-Java诊断利器Arthas排查问题实践》电子版地址
|
物联网 开发者 智能硬件
|
Java Linux Windows
深度探索JFR - JFR详细介绍与生产问题定位落地 - 1. JFR说明与启动配置(下)
深度探索JFR - JFR详细介绍与生产问题定位落地 - 1. JFR说明与启动配置(下)
深度探索JFR - JFR详细介绍与生产问题定位落地 - 1. JFR说明与启动配置(下)
|
存储 缓存 监控
深度探索JFR - JFR详细介绍与生产问题定位落地 - 1. JFR说明与启动配置(上)
深度探索JFR - JFR详细介绍与生产问题定位落地 - 1. JFR说明与启动配置(上)
深度探索JFR - JFR详细介绍与生产问题定位落地 - 1. JFR说明与启动配置(上)
|
Java jenkins Linux
企业DevOps之路:java maven项目编译配置二
本篇内容接着上一篇的继续讲解,完成 java 的 maven 项目的构建。
186 0
企业DevOps之路:java maven项目编译配置二
|
Java jenkins Devops
企业DevOps之路:java maven项目编译配置一
本篇对编译一个 java 的 maven 项目做一些前期的配置工作的讲解。
147 0
企业DevOps之路:java maven项目编译配置一
|
运维 监控 UED
持续演进的Cloud Native (读书笔记01)
观察任何一个企业都可以从三个角度出发,这三个角度分别是技术、流程、文化,三个方面都做好才能成为伟大的企业。Cloud Native也一样,需要从架构、研发流程、团队文化三个角度来实现,三者需要相互配合,缺一不可。
持续演进的Cloud Native (读书笔记01)
下一篇
无影云桌面