JVM插码之五:Java agent+ASM实战--监控所有方法执行时间
本文建立在对instrumentation和agent有初步的了解的前提下阅读,关于这2个类的讲解在其它文章中。
这是一个maven项目,pom中需要的配置,lib中有asm的jar包
pom.xml文件:
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <groupId>com.dxz</groupId>
<artifactId>chama</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging> <name>chama</name>
<url>http://maven.apache.org</url> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties> <dependencies>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-test</artifactId>
<version>6.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-analysis</artifactId>
<version>6.2</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-commons</artifactId>
<version>6.2</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-tree</artifactId>
<version>6.2</version>
</dependency>
<dependency>
<groupId>org.ow2.asm</groupId>
<artifactId>asm-util</artifactId>
<version>6.2</version>
</dependency>
<dependency>
<groupId>javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.12.1.GA</version>
</dependency> <!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.5</version>
</dependency> <!-- https://mvnrepository.com/artifact/oro/oro -->
<dependency>
<groupId>oro</groupId>
<artifactId>oro</artifactId>
<version>2.0.8</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
</dependencies>
<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-shade-plugin</artifactId>
<version>3.0.0</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<transformers>
<transformer
implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<manifestEntries>
<!-- <Premain-Class>com.dxz.chama.javaagent.patter.TimeMonitorPatterAgent</Premain-Class> -->
<!-- <Premain-Class>com.dxz.chama.javaagent.StatAgent</Premain-Class> -->
<Premain-Class>com.dxz.chama.javaagent.asm.UdAgent</Premain-Class>
</manifestEntries>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
打包后的agent的jar包中manifest属性如下:
agent类,只有一个方法,就是把自定义的类修改器添加到instrumentation中。
package com.dxz.chama.javaagent.asm; import java.lang.instrument.Instrumentation; public class UdAgent {
public static void premain(String agentArgs, Instrumentation instrumentation){
instrumentation.addTransformer(new LogTransformer());
}
}
类转换器实现:
package com.dxz.chama.javaagent.asm; import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain; import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter; public class LogTransformer implements ClassFileTransformer {
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
try {
ClassReader cr = new ClassReader(className);
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
TimeCountAdpter timeCountAdpter = new TimeCountAdpter(cw); cr.accept(timeCountAdpter, ClassReader.EXPAND_FRAMES); return cw.toByteArray();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
实际修改字节码的方法,这里给每个类添加了一个字段UDASMCN,用于记录当前类的名字(方便打印信息)。同时记录每个方法的名字,以及执行时间。
package com.dxz.chama.javaagent.asm; import org.objectweb.asm.ClassVisitor;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.commons.AnalyzerAdapter;
import org.objectweb.asm.commons.LocalVariablesSorter; public class TimeCountAdpter extends ClassVisitor implements Opcodes {
private String owner;
private boolean isInterface;
private String filedName = "UDASMCN";
private int acc = Opcodes.ACC_PUBLIC + Opcodes.ACC_STATIC + Opcodes.ACC_FINAL;
private boolean isPresent = false; private String methodName; public TimeCountAdpter(ClassVisitor classVisitor) {
super(ASM6, classVisitor);
} @Override
public void visit(int version, int access, String name, String signature, String superName, String[] interfaces) {
super.visit(version, access, name, signature, superName, interfaces);
owner = name;
isInterface = (access & ACC_INTERFACE) != 0;
} @Override
public FieldVisitor visitField(int access, String name, String descriptor, String signature, Object value) {
if (name.equals(filedName)) {
isPresent = true;
}
return super.visitField(access, name, descriptor, signature, value);
} @Override
public MethodVisitor visitMethod(int access, String name, String descriptor, String signature,
String[] exceptions) {
MethodVisitor mv = cv.visitMethod(access, name, descriptor, signature, exceptions); if (!isInterface && mv != null && !name.equals("<init>") && !name.equals("<clinit>")) {
methodName = name;
AddTimerMethodAdapter at = new AddTimerMethodAdapter(mv);
at.aa = new AnalyzerAdapter(owner, access, name, descriptor, at);
at.lvs = new LocalVariablesSorter(access, descriptor, at.aa); return at.lvs;
} return mv;
} public void visitEnd() {
if (!isInterface) {
FieldVisitor fv = cv.visitField(acc, filedName, "Ljava/lang/String;", null, owner);
if (fv != null) {
fv.visitEnd();
}
}
cv.visitEnd();
} class AddTimerMethodAdapter extends MethodVisitor {
private int time;
private int maxStack;
public LocalVariablesSorter lvs;
public AnalyzerAdapter aa; public AddTimerMethodAdapter(MethodVisitor methodVisitor) {
super(ASM6, methodVisitor);
} @Override
public void visitCode() {
mv.visitCode();
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
time = lvs.newLocal(Type.LONG_TYPE);
mv.visitVarInsn(LSTORE, time);
maxStack = 4;
} @Override
public void visitInsn(int opcode) {
if (((opcode >= IRETURN && opcode <= RETURN) || opcode == ATHROW) && !isPresent) {
mv.visitMethodInsn(INVOKESTATIC, "java/lang/System", "nanoTime", "()J", false);
mv.visitVarInsn(LLOAD, time);
mv.visitInsn(LSUB);
mv.visitVarInsn(LSTORE, time); mv.visitFieldInsn(GETSTATIC, "java/lang/System", "out", "Ljava/io/PrintStream;"); mv.visitTypeInsn(NEW, "java/lang/StringBuilder");
mv.visitInsn(DUP);
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuilder", "<init>", "()V", false);
mv.visitFieldInsn(GETSTATIC, owner, filedName, "Ljava/lang/String;");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); mv.visitLdcInsn(" " + methodName + ":");
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append",
"(Ljava/lang/String;)Ljava/lang/StringBuilder;", false); mv.visitVarInsn(LLOAD, time);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "append", "(J)Ljava/lang/StringBuilder;",
false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/lang/StringBuilder", "toString", "()Ljava/lang/String;", false);
mv.visitMethodInsn(INVOKEVIRTUAL, "java/io/PrintStream", "println", "(Ljava/lang/String;)V", false); maxStack = Math.max(aa.stack.size() + 4, maxStack);
}
mv.visitInsn(opcode);
} @Override
public void visitMaxs(int maxStack, int maxLocals) {
super.visitMaxs(Math.max(maxStack, this.maxStack), maxLocals);
}
} }
打包成jar包后,在另一个程序启动时调用,启动参数如下:
-javaagent:D:\study\chama\target\chama-0.0.1-SNAPSHOT.jar
执行效果:
时间单位是纳秒,可以看到每个方法执行完时,都会打印这个方法 的执行时间,以com/Main main:11457636为例,说明类com/Main的main方法执行力11毫秒。
JVM插码之五:Java agent+ASM实战--监控所有方法执行时间的更多相关文章
- Java 调式、热部署、JVM 背后的支持者 Java Agent
我们平时写 Java Agent 的机会确实不多,也可以说几乎用不着.但其实我们一直在用它,而且接触的机会非常多.下面这些技术都使用了 Java Agent 技术,看一下你就知道为什么了. -各个 J ...
- JVM插码之四:Java动态代理机制的对比(JDK 和CGLIB,Javassist,ASM)
一.class文件简介及加载 Java编译器编译好Java文件之后,产生.class 文件在磁盘中.这种class文件是二进制文件,内容是只有JVM虚拟机能够识别的机器码.JVM虚拟机读取字节码文件, ...
- JVM插码之三:javaagent介绍及javassist介绍
本文介绍一下,当下比较基础但是使用场景却很多的一种技术,稍微偏底层点,就是字节码插庄技术了...,如果之前大家熟悉了asm,cglib以及javassit等技术,那么下面说的就很简单了...,因为下面 ...
- JVM插码之六:jacoco插码及问题“$jacocodata 属性 Method not found: is$jacocoData”
在使用jacoco统计自动化代码覆盖率 jacoco统计自动化代码覆盖率 1. 简介1.1. 什么是JacocoJacoco是一个开源的代码覆盖率工具,可以嵌入到Ant .Maven中,并提供了Ecl ...
- 基于Java Agent的premain方式实现方法耗时监控(转),为了找到结论执行:premain在jvm启动的时候执行,所有方法前,会执行MyAgent的premain方法
Java Agent是依附于java应用程序并能对其字节码做相关更改的一项技术,它也是一个Jar包,但并不能独立运行,有点像寄生虫的感觉.当今的许多开源工具尤其是监控和诊断工具,很多都是基于Java ...
- jvm源码解析java对象头
认真学习过java的同学应该都知道,java对象由三个部分组成:对象头,实例数据,对齐填充,这三大部分扛起了java的大旗对象,实例数据其实就是我们对象中的数据,对齐填充是由于为了规则分配内存空间,j ...
- jvm源码解读--20 结合jvm源码理解 java 设计模式 模板方法
write by 张艳涛 前言: 在学习jvm之前,看过设计模式的书,知道模板方法的设计模式,今天在看java并发编程的艺术里面关于AbstractQueuedSynchronizer 用法,这个就使 ...
- 🏆【Java技术专区】「探针Agent专题」Java Agent探针的技术介绍(1)
前提概要 Java调式.热部署.JVM背后的支持者Java Agent: 各个 Java IDE 的调试功能,例如 eclipse.IntelliJ : 热部署功能,例如 JRebel.XRebel. ...
- 探秘 Java 热部署二(Java agent premain)
# 前言 在前文 探秘 Java 热部署 中,我们通过在死循环中重复加载 ClassLoader 和 Class 文件实现了热部署的功能,但我们也指出了缺点-----不够灵活.需要手动修改文件等操作. ...
随机推荐
- Log4j在Java工程中使用方法
Eclipse新建Java工程,工程目录如下 1.下载log4j的Jar包,在Java工程下新建lib文件夹,将jar包拷贝到此文件夹,并将其加入到路径中,即:Jar包上右键——Build Path— ...
- 网络协议栈学习(一)socket通信实例
网络协议栈学习(一)socket通信实例 该实例摘自<linux网络编程>(宋敬彬,孙海滨等著). 例子分为服务器端和客户端,客户端连接服务器后从标准输入读取输入的字符串,发送给服务器:服 ...
- STL标准库-一个万用的hash function
技术在于交流.沟通,本文为博主原创文章转载请注明出处并保持作品的完整性 在前面我介绍过hash的使用,本次主要介绍一下Hash Function Hash Function即获得hash code的函 ...
- mysql sum(if())用法
原表:id fenlei time1 分类1 201303162 分类2 201303163 分类3 201303174 ...
- Python_常用的正则表达式处理函数
正则表达式就是用查找字符串的,它能查找规则比较复杂的字符串反斜杠:正则表达式里面用"\"作为转义字符. s='<a class="h3" href=&qu ...
- C语言基础:内存 分类: iOS学习 c语言基础 2015-06-10 21:59 23人阅读 评论(0) 收藏
全局变量:定义在函数之外.(不安全) 局部变量;定义在函数之内. 内存的划分:1栈区 2堆区 3静态区(全局区) 4常量区 5代码区 栈区..静态区.常量区.代码区的数据都是由系统分配和释放 ...
- ios开发过程中描述文件(provisioning profile)过期导致ios无法正常安装的处理办法
1.登录开发者中心,重新编辑描述文件,获得最新的描述文件.(如果对应的P12文件也过期,需要同时下载最新的p12文件).----该步骤需要有权限的人才能操作. 2.下载最新的描述文件和p12文件(如果 ...
- Error: timed out while waiting for target halted
/************************************************************************************ * Error: timed ...
- 如何设置鼠标右键单击返回ppt上一页
点击“powerpoint选项”,选择“高级” 将“幻灯片放映”选项下“鼠标右键单击时显示菜单(E)”前面的钩去掉.图为处理过的.
- 20155229 2016-2017-2 《Java程序设计》第五周学习总结
20155229 2016-2017-2 <Java程序设计>第五周学习总结 教材学习内容总结 第八章: Java中所有错误都会被打包为对象. 设计错误对象都继承自java.lang.Th ...