技术经验分享: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());


}


}</

相关文章
|
9月前
|
域名解析 运维 网络协议
Linux命令行全景指南:从入门到实践,掌握命令行的力量
Linux命令行全景指南:从入门到实践,掌握命令行的力量
209 0
|
5月前
|
Java 编译器 Maven
【颠覆你的认知!】当Quarkus邂逅GraalVM本机镜像,应用启动竟快到飞起——背后的技术秘密等你揭秘!
Quarkus框架因轻量级与高性能而在Java开发社区广受关注。结合GraalVM使用能显著提升应用启动速度与运行效率,这得益于GraalVM的本机镜像支持。本文将介绍如何利用Quarkus和GraalVM构建高效应用,并提供示例代码演示具体步骤。首先需安装GraalVM环境并配置Maven支持构建本机镜像。接着创建一个简单的RESTful服务端点作为示例,通过命令行编译生成本机可执行文件并运行。这种方式能够大幅提升应用性能,但需注意构建时间和部分Java特性兼容性问题。
82 1
|
6月前
|
开发者 Java 安全
Struts 2 实战秘籍:Action 驱动业务,配置文件成就高效开发!
【8月更文挑战第31天】Struts 2 框架作为 Apache 软件基金会的顶级项目,广泛应用于企业级 Web 应用开发。其核心组件 Action 类处理用户请求,而配置文件定义请求与 Action 类间的映射关系。掌握 Action 组件的最佳实践包括继承 `ActionSupport` 类、实现 `execute` 方法及使用类型安全的方法;配置文件的最佳实践则涉及组织 Action 到包中、定义全局结果及使用通配符映射。遵循这些最佳实践,可构建高效、可维护的 Web 应用程序。
57 0
|
8月前
|
Windows
技术经验分享:bootsect命令
技术经验分享:bootsect命令
|
Arthas 监控 安全
浅谈阿里开源JVM Sandbox(内含代码实战)
浅谈阿里开源JVM Sandbox(内含代码实战)
47302 6
浅谈阿里开源JVM Sandbox(内含代码实战)
|
前端开发 Java 物联网
GIAC-2022sh 学习笔记 | WebAssembly在前端中的应用与展望
GIAC-2022sh 学习笔记 | WebAssembly在前端中的应用与展望
386 0
GIAC-2022sh 学习笔记 | WebAssembly在前端中的应用与展望
|
缓存 算法 Java
JVM技术实战-带你学习一下TomcatGC的配置
JVM技术实战-带你学习一下TomcatGC的配置
230 0
|
消息中间件 缓存 算法
持续演进的Cloud Native (读书笔记03)
可靠性公式:A=MTBF /(MTBF+MTTR)。其中,MTBF的全称是Mean Time Between Failure,即平均无故障工作时间,指上一次故障恢复后开始正常运行到这次故障的时间平均值。MTTR的全称是Mean Time ToRepair,即平均故障修复时间,是指从出现故障到完全恢复的这段时间。
持续演进的Cloud Native (读书笔记03)
|
存储 缓存 网络协议
持续演进的Cloud Native (读书笔记02)
微服务架构并不是什么技术创新,而是开发过程发展到一定阶段对技术架构的要求,是在实践中不断摸索而来的。
持续演进的Cloud Native (读书笔记02)
|
运维 监控 UED
持续演进的Cloud Native (读书笔记01)
观察任何一个企业都可以从三个角度出发,这三个角度分别是技术、流程、文化,三个方面都做好才能成为伟大的企业。Cloud Native也一样,需要从架构、研发流程、团队文化三个角度来实现,三者需要相互配合,缺一不可。
持续演进的Cloud Native (读书笔记01)