java instrumentation &JVMTI

Java Instrumentation (参考:http://www.ibm.com/developerworks/cn/java/j-lo-jse61/

简介:

使用Instrumentation,开发者可以构建独立于应用程序的代理程序,用来检测和协助运行在JVM上的程序,甚至能够替换和修改某些类的定义

遇到问题:

运行在tomcat容器中的应用程序,由于main方法是在Bootstrap.jar中容器启动时执行的,

Agent-class方式在替换的方法中只获取到tomcat相关jar包中的一些类:

上图中是在transform方法中打印出来的,这个方法会在二中介绍

具体介绍:

下面是两种方式PreMain 和 agentMain,分别为main函数执行之前,和执行之后的操作

一:PreMain

main函数执行之间,扫描判断特定的类,然后以字节数组的方式加载代理类的字节码文件,替换目标类:

示例代码:

public class TransClass {
    public int getNumber() {
        return 2;
    }
}

TransClass

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;

class Transformer implements ClassFileTransformer {
    public static final String classNumberReturns2 = "D://AOP//TransClass.class.2";
    public static byte[] getBytesFromFile(String fileName) {
    try {
        // precondition
        File file = new File(fileName);
        InputStream is = new FileInputStream(file);
        long length = file.length();
        byte[] bytes = new byte[(int) length];

        // Read in the bytes
        int offset = 0;
        int numRead = 0;
        while (offset <bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
            offset += numRead;
        }
        if (offset < bytes.length) {
            throw new IOException("Could not completely read file "+ file.getName());
        }
        is.close();
        return bytes;
        } catch (Exception e) {
            System.out.println("error occurs in _ClassTransformer!"+ e.getClass().getName());
            return null;
        }
    }

    public byte[] transform(ClassLoader l, String className, Class<?> c,
        ProtectionDomain pd, byte[] b) throws IllegalClassFormatException {
        if (!className.equals("TransClass")) {
            return null;
        }
        return getBytesFromFile(classNumberReturns2);
    }
} 

Transformer

public class TestMainInJar {
    public static void main(String[] args) {
        System.out.println(new TransClass().getNumber());
    }
}

TestMainInJar

import java.lang.instrument.UnmodifiableClassException;
import java.lang.instrument.Instrumentation;
public class Premain {
public static void premain(String agentArgs, Instrumentation inst)
    throws ClassNotFoundException, UnmodifiableClassException {
        inst.addTransformer(new Transformer());
    }
} 

Premain

Manifest-Version: 1.0
Premain-Class: Premain

MANIFEST.MF

步骤:

1. TransClass.java 中 改成 return 2; 之后编译生成的TransClass.class改名为TransClass.class.2 ,以防止跟原TransClass.class重名

2. 打包:jar -cvf0 TestInstrument1.jar TransClass.class Transformer.class TestMainInJar.class Premain.class

打好jar后替换manifest.mf文件,(没找到怎么把这个文件直接打进jar包)

3. 命令行执行

1)java -javaagent:TestInstrument1.jar -cp TestInstrument1.jar TestMainInJar

2)java -cp TestInstrument1.jar TestMainInJar

 (TransClass.class.2 和 TestInstrument1.jar 需要放置到  D:\AOP 目录下)

用第一个命令执行的时候,会把TransClass.class.2的内容加载进来替换jar包中的TransClass.class,而第二个命令是正常执行jar包中的TransClass.class

二: Agent-class

import java.lang.instrument.Instrumentation;

public class LoadedAgent {
    @SuppressWarnings("rawtypes")
    public static void agentmain(String args, Instrumentation inst){
        Class[] classes = inst.getAllLoadedClasses();
    //inst.addTransformer(new Transformer());
        for(Class cls :classes){
            System.out.println(cls.getName());
        }
    }
}

LoadedAgent

public class TargetVM {
    public static void main(String[] args) throws InterruptedException{
        while(true){
            Thread.sleep(1000);
        }
    }
}

TargetVM

import java.io.IOException;

import com.sun.tools.attach.AgentInitializationException;
import com.sun.tools.attach.AgentLoadException;
import com.sun.tools.attach.AttachNotSupportedException;
import com.sun.tools.attach.VirtualMachine;

public class Test {
    public static void main(String[] args) throws AttachNotSupportedException,
            IOException, AgentLoadException, AgentInitializationException {
        VirtualMachine vm = VirtualMachine.attach("1244");
        vm.loadAgent("D:/AOP/agentmain/agentAop.jar");

    }

}

Test

Manifest-Version: 1.0
Agent-Class: LoadedAgent

MANIFEST.MF

步骤:

1. 将LoadedAgent.class 和 Manifest.mf打进jar包

2. 执行TargetVM.class 获取进程号PID

3. 执行Test.class pid作为参数

  

注意:

1. 编译时需要用到jdk中lib目录下的tools.jar  javac -cp tools.jar Test.java

2. 执行命令(1244 是进程号) :java -classpath "D:/AOP/agentmain/tools.jar" Test 1244

java instrumentation &JVMTI

时间: 2024-10-06 18:00:31

java instrumentation &JVMTI的相关文章

Java Instrumentation

说明:本博文是博主学习 Instrumentation 历程的总结,整合了学习过程中参考的关于Instrumentation 的教程,并加入博主自己的见解和实例. 参考链接: Instrumentation 新功能 JDK源码-java.lang.instrument-第一部分-源码学习 Instrumentation 简介 利用 Java 代码,即 java.lang.instrument 做动态 Instrumentation 是 Java SE 5 的新特性,它把 Java 的 instr

极客时间-左耳听风-程序员攻略-Java底层知识

Java 字节码相关 字节码编程,也就是动态修改或是动态生成 Java 字节码.Java 的字节码相当于汇编,其中的一些细节. Java Zone: Introduction to Java Bytecode ,这篇文章图文并茂地讲述了 Java 字节码的一些细节. IBM DeveloperWorks: Java bytecode ,讲 Java 字节码的文章. Java Bytecode and JVMTI Examples,这是一些使用 JVM Tool Interface 操作字节码的比

java agent技术原理及简单实现

1.引子 在某天与QA同学进行沟通时,发现QA同学有针对某个方法调用时,有让该方法停止一段时间的需求,我对这部分的功能实现非常好奇,因此决定对原理进行一些深入的了解,力争找到一种使用者尽可能少的对原有代码进行修改的方式,以达到对应的MOCK要求. 整体的感知程度可以分为三个级别: 硬编码 增加配置 无需任何修改 2.思路 在对方法进行mock,暂停以及异常模拟,在不知道其原理的情况下,进行猜想,思考其具体的实现原理,整体来说,最简单的实现模型无外乎两种: 2.1 朴素思路 假设存在如下的函数 p

Java 中的纤程库 – Quasar

来源:鸟窝, colobu.com/2016/07/14/Java-Fiber-Quasar/ 如有好文章投稿,请点击 → 这里了解详情 最近遇到的一个问题大概是微服务架构中经常会遇到的一个问题: 服务 A 是我们开发的系统,它的业务需要调用 B.C.D 等多个服务,这些服务是通过http的访问提供的. 问题是 B.C.D 这些服务都是第三方提供的,不能保证它们的响应时间,快的话十几毫秒,慢的话甚至1秒多,所以这些服务的Latency比较长.幸运地是这些服务都是集群部署的,容错率和并发支持都比较

Java的纤程库 - Quasar

最近遇到的一个问题大概是微服务架构中经常会遇到的一个问题: 服务 A 是我们开发的系统,它的业务需要调用 B . C . D 等多个服务,这些服务是通过http的访问提供的. 问题是 B . C . D 这些服务都是第三方提供的,不能保证它们的响应时间,快的话十几毫秒,慢的话甚至1秒多,所以这些服务的Latency比较长.幸运地是这些服务都是集群部署的,容错率和并发支持都比较高,所以不担心它们的并发性能,唯一不爽的就是就是它们的Latency太高了. 系统A会从Client接收Request,

曹工说Spring Boot源码(13)-- AspectJ的运行时织入(Load-Time-Weaving),基本内容是讲清楚了(附源码)

写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean Definition到底是什么,咱们对着接口,逐个方法讲解 曹工说Spring Boot源码(3)-- 手动注册Bean Definition不比游戏好玩吗,我们来试一下 曹工说Spring Boot源码(4)-- 我是怎么自定义ApplicationContext,从json文件读取bean de

[深入理解Android卷一全文-第八章]深入理解Surface系统

由于<深入理解Android 卷一>和<深入理解Android卷二>不再出版.而知识的传播不应该由于纸质媒介的问题而中断,所以我将在CSDN博客中全文转发这两本书的全部内容. 第8章  深入理解Surface系统 本章主要内容 ·  具体分析一个Activity的显示过程. ·  具体分析Surface. ·  具体分析SurfaceFlinger. 本章涉及的源代码文件名称及位置: · ActivityThread.java framework/base/core/java/an

Android 手机自动化测试工具有哪几种?

Sikuli (http://sikuli.org),基于优秀的图像对比库opencv的测试工具,测试脚本使用Python编写,非常强大.如果你的app没有源码,可以选择它:或者你想做系统测试(跨app的测试),也可以选择它.其它的还是用下面说的那些个吧 应用层的 先说说开源的吧: Robotium Monkeyrunner Robolectric CTS 还有个新兴的测试工具,以前在GitHub看到,现在找不到了,好像是BDD类型的语法:现在还不成熟. 另外基于web的测试也有基于Seleni

JVM Management API

JVM本身提供了一组管理的API,通过该API,我们可以获取得到JVM内部主要运行信息,包括内存各代的数据.JVM当前所有线程及其栈相关信 息等等.各种JDK自带的剖析工具,包括jps.jstack.jinfo.jstat.jmap.jconsole等,都是基于此API开发的.本篇对 这部分内容进行一个详细的说明. 参考:http://java.sun.com/javase/6/docs/api/java/lang/management/package-summary.html 一.Manage