Java Instrument

指针极昼
• 阅读 934

JVMTI

什么是JVMTI

JVM Tool Interface简称JVMTI是一组对外接口,通过这组接口可以实现,获取虚拟机运行状态、线程分析、监控、调试、覆盖率分析等功能。

JVMTIAgent

什么是JVMTIAgent

为了使用JVMTI提供的对外接口,一般采用Agent方式来实现JVMTI提供的对外接口,JVMTIAgent类似于c语言的动态库的概念。

实现方式

Java1.5之前实现一个Agent只能通过原生的c/c++来实现Agent,在Java1.5之后提供了instrumentagent,也叫做JPLISAgent(Java Programming Language Instrumentation Services Agent)专门用于Java方式。

启动方式

Agent有两种启动方式

  • 第一种是在jvm启动的时候,指定agent程序的位置来启动。
  • 另外一种方式是jvm已经在运行了,使用attach的方式到目标进程里面。在java1.5的时候只支持jvm启动,在java1.6的时候支持attach的方式启动,在jvmtool.jar里面提供了工具VirtualMachine来帮助启动agent

Instrument

什么是Instrument

Instrument提供了为Java编程语言插入代码的服务,Instrumentation是在方法中添加字节码,以便收集使用的数据,由于这些改变是添加字节码,不会修改程序的状态或者行为。比如监视器代码、探查器、覆盖率分析器和事件记录器。

Instrument只是提供插入代码服务,在方法中添加字节码,至于具体的字节码操作,是由字节码操作工具来实现的,常见的字节码操作工具包括:CGLIBJavassistASM等。

获取Instrumentation实例

指定接收类

要获取Instrumentation实例,首先要指定将Instrumentation实例传递给哪个类,有两种方式来指定传递给这个类。

  • 第一种方式是在配置文件resource\META_INF\MANIFEST.MF中指定。

    Manifest-Version: 1.0
    Can-Redefine-Classes: true
    Can-Retransform-Classes: true
    Premain-Class: com.lee.agent.PreMainAgent
    Agent-Class: com.lee.agent.PreMainAgent
  • 第二种方式是在pom文件中指定,本质上也是在配置MANIFEST.MF文件

    <plugin>
      <excutions>
        <excution>
          <archive>
              <manifestFile>
              <Premain-Class>com.lee.agent.PreMainAgent</Premain-Class>
              <Agent-Class>com.lee.agent.PreMainAgent</Agent-Class>
            </manifestFile>
          </archive>
        </excution>
      </excutions>
    </plugin>

指定接收方法

  • JVM以指定代理类的方式启动,在这种情况下Instrumentation实例被传给代理类的premain方法;

    public static void premain(String agentArgs, Instrumentation inst);
    public static void premain(String agentArgs);
  • JVM启动后,以attach的方式指定代理类,在这种情况下Instrumentation实例被传递给代理类的agentmain方法。

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

示例代码

整体流程图示

Java Instrument

目标程序

目标程序是被操作的程序,被修改的是目标类TargetClass

public class Demo {
    public static void main(String[] args) throws Exception {
        TargetClass targetClass = new TargetClass();
        targetClass.targetMethod();
    }
}
public class TargetClass {
    public String targetMethod() {
        System.out.println("执行测试方式");
        return "return";
    }
}

Agent程序

public class PreMainAgent {
    /**
     * 指定agentjar包启动,Instrument实例会传递给这个方法
     */
    public static void premain(String agentArgs, Instrumentation inst){
        customLogic(inst);
    }
    /**
     * attach方法启动,Instrument实例会传递给这个方法
     */ 
    public static void agentmain(String agentArgs, Instrumentation inst){
        customLogic(inst);
    }
    private static void customLogic(Instrumentation inst){
        inst.addTransformer(new MyClassTransformer(), true);
    }
}
class MyClassTransformer implements ClassFileTransformer {

    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) {
        final ClassPool classPool = ClassPool.getDefault();
        CtClass clazz;
        CtMethod ctMethod;
        try {
            if ("com/lee/TargetClass".equals(className)){
                clazz = classPool.get("com.lee.TargetClass");
                ctMethod = clazz.getDeclaredMethod("targetMethod");
                ctMethod.insertBefore("System.out.println(\"****************\");");
                byte[] byteCode = clazz.toBytecode();
                clazz.detach();
                return byteCode;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}

启动

  • 先将agent项目打包成一个jar包,agent.jar
  • 两种启动方式

    • 在启动目标程序的时候指定agent的位置:-javaagent:jar包路径\Jagent.jar
    • attach方式启动

      // project1启动的pid
      VirtualMachine vm = VirtualMachine.attach("1856");
      vm.loadAgent("jar包路径\Jagent.jar");
点赞
收藏
评论区
推荐文章
Wesley13 Wesley13
3年前
MySQL部分从库上面因为大量的临时表tmp_table造成慢查询
背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_
美凌格栋栋酱 美凌格栋栋酱
7个月前
Oracle 分组与拼接字符串同时使用
SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(
Wesley13 Wesley13
3年前
UIWebView长按保存图片和识别图片二维码的实现方案(使用缓存)
0x00需求:长按识别UIWebView中的二维码,如下图长按识别二维码0x01方案1:给UIWebView增加一个长按手势,激活长按手势时获取当前UIWebView的截图,分析是否包含二维码。核心代码:略优点:流程简单,可以快速实现。不足:无法实现保存UIWebView中图片,如果当前We
灯灯灯灯 灯灯灯灯
4年前
阿里二面必备考题之Java并发!全面解析
一、使用线程有三种使用线程的方法:实现Runnable接口实现Callable接口继承Thread类实现Runnable和Callable接口的类只能当做一个可以在线程中运行的任务,不是真正意义上的线程,因此最后还需要通过Thread来调用。可以理解为任务是通过线程驱动从而执行的。实现Runnable接口cpublicclass
九鹤 九鹤
4年前
并发编程的基础概念
什么是线程?什么是进程?java可以开启线程吗?不能因为Java无法直接操硬件,他是运行在虚拟机上面的,什么是并发?什么是并行?并发就是多个线程去操作一个资源。并行是多个线程同时行,但是操作的资源不是同一个。线程的六个状态new(诞生)runnable(运行)Blocked(阻塞)waiiiing(等待)Tiemwaiing(超时等待)
Stella981 Stella981
3年前
Linux自动检测网站心跳通知shell脚本
!/bin/bashLIST("http://xxxx.com")NAME("评价系统getwindowList接口")for((i0;i<${LIST@};i))doHTTP_CODEcurlo/dev/nullsw"%{http_code}""${LIST
Wesley13 Wesley13
3年前
03.Android崩溃Crash库之ExceptionHandler分析
目录总结00.异常处理几个常用api01.UncaughtExceptionHandler02.Java线程处理异常分析03.Android中线程处理异常分析04.为何使用setDefaultUncaughtExceptionHandler前沿上一篇整体介绍了crash崩溃
Wesley13 Wesley13
3年前
Java黑科技之源:JVMTI完全解读
Java生态中有一些非常规的技术,它们能达到一些特别的效果。这些技术的实现原理不去深究的话一般并不是广为人知。这种技术通常被称为黑科技。而这些黑科技中的绝大部分底层都是通过JVMTI实现的。形象地说,JVMTI是Java虚拟机提供的一整套后门。通过这套后门可以对虚拟机方方面面进行监控,分析。甚至干预虚拟机的运行。下面先介绍下哪些黑科技是通过JVMTI
Wesley13 Wesley13
3年前
JAVA程序设计练习题集答案
一、判断题1.String字符串在创建后可以被修改。(0)2.引用一个类的属性或调用其方法,必须以这个类的对象为前缀。(0final类名)3.当调用一个正在进行线程的stop()方法时,该线程便会进入休眠状态。(0)4.如果一个类声明实现一个接口,但没有实现接口中的所有方法,那么这个类必须是abst
进程还在,JSF接口不干活了,这你敢信?
1、问题背景:应用在配合R2m升级redis版本的过程中,上游反馈调用接口报错,RpcException:2)分析栈信息可以用在线分析工具:2.1)分析线程状态通过工具可以定位到JSF线程大部分卡在JedisClusterInfoCachegetSlave
五、飞鹅官网API接口文档
接口列表1.获取网站信息请求方法:GET请求URL:/api/site/getSiteInfo请求参数无返回结果json"code":1,"data":"id":1,//id"title":"SampleSiteName",//网站名称"intro":"T