使用JRebel启动工程时加上VM参数时有一个参数是"-javaagent:D:\jrebel_5.6.0\jrebel.jar"。

javaagent是什么? java -help后看到如下信息:

Java代理不是应用程序中的一部分,instrument支持Java以代理的形式监控或重新定义运行中的服务。
我们可以在不修改程序代码的前提下通过Instrumentation API改变运行中的java程序。
而我们使用的"-javaagent:jarpath[=options] "参数则是一种方式,也就是在应用程序启动前执行代理。

参数值是个jar路径,先让我们试着写一个代理,再将其导出为jar.
鉴于没有什么实际目的,那就以java agent的使用为重点,尽量写得简单一些。
所谓agent的代码实现就是一个带premain()方法的类,但这个方法并不是根据某个抽象,而是硬生生地定义到类里。
premain方法的声明可以是public static void premain(String args, Instrumentation inst)或 public static void premain(String args)。
参数String args对应"-javaagent:jarpath[=options] "中的options。
参数Instrumentation inst则是运行时传入的Instrumentation实例。

package pac.testcase.basic.agent;

import java.lang.instrument.Instrumentation;

public class TestAgent {
public static void premain(String agentArgs, Instrumentation inst){
System.out.println("Alvez Agent:::");
}
}

将TestAgent导出为jar,假设我的导出路径是"D:/myAgent.jar"。
随便写一个main方法,加上参数"-javaagent:D:/myAgent.jar"并运行:

package pac.testcase.basic.agent;

public class TestAgentMain {
public static void main(String[] args) throws InterruptedException {
System.out.println("code in runtime::");
}
}

结果提示:
Error occurred during initialization of VM
agent library failed to init: instrument
Failed to find Premain-Class manifest attribute in D:/myAgent.jar

需要在MANIFEST.MF中在加一条属性,手动指定premain class,比如本例中改为如下:
Manifest-Version: 1.0
Premain-Class: pac.testcase.basic.agent.TestAgent
Can-Redefine-Classes: true

接下来就可以运行了,结果输出:
Alvez Agent:::
code in runtime::

但只是这样没什么意思,我们只能在应用程序运行之前指定premain class,结果还是没有做到运行时进行改变,有没有办法在运行后指定代理程序?
程序运行时发生不曾预料的情况,此时用代理临时解决突发状况才更加有意义,但解决这一问题的并不是premain而是agentmain。
和premain相似的是,方法声明几乎相同,即public static void agentmain(String args, Instrumentation inst)或 public static void agentmain(String args)。

package pac.testcase.basic.agent;

import java.lang.instrument.Instrumentation;

public class TestAgent {
public static void agentmain(String agentArgs, Instrumentation inst){
System.out.println("Alvez Agent:::");
}
}

另外,在MANIFEST指定的属性名也改为Agent-Class,如下:
Manifest-Version: 1.0
Agent-Class: pac.testcase.basic.agent.TestAgent
Can-Redefine-Classes: true
(Ps:即使同时指定了Premain-Class和Agent-Class也不会有问题。)

和Premain不同的是,使用Agentmain时我们需要使用tools.jar中的com.sun.tools.attach.VirtualMachine,将代理jar绑定到指定VM,比如:

VirtualMachine vm  = VirtualMachine.attach("");
vm.loadAgent("");

获得相应的VM,并通过loadAgent指定agentmain jar。

两者都用Instrumentation

光看了下该方法的声明,没想太多,弄了个premain方法初始化Instrumentation:

public class ObjectSizeUtils {

    private static Instrumentation inst = null;

    public static void premain(String agentArgs, Instrumentation inst) {
ObjectSizeUtils.inst = inst;
} public static long sizeOf(Object o){
if(inst == null) throw new IllegalStateException("not initialized yet");
return inst.getObjectSize(o);
}
}

export jar并在MANIFEST.MF中加上:

Premain-class: pac.testcase.utils.ObjectSizeUtils
Can-Redefine-Classes: false

随便写一个实体类,用来测试:

class Person{
private String name;
private String life; public String getName() {return name;}
public void setName(String name) {this.name = name;}
public String getLife() {return life;}
public void setLife(String life) {this.life = life;}
}

好了,在运行参数中加上-javaagent,执行看看会输出什么:

    @Test
public void testSizeUtils(){
Person person = new Person("Alvez","make me death before you make me old");
logger.info(String.valueOf(sizeOf(person)));
logger.info(String.valueOf(sizeOf(person.getName())));
logger.info(String.valueOf(sizeOf(person.getLife())));
} static final Logger logger = LoggerFactory.getLogger(ObjectSizeUtils.class);

结果很奇怪...

为什么person的属性比person还大? 等等,name和life的大小好像也有问题....
回去翻教科书发现事情并不是我希望的那样,我需要把对象里面的元素的大小全部加起来才能达到我要的效果....
看来还需要亲自解决一些事情,于是:

public class ObjectSizeUtils {

    private static Instrumentation inst = null;

    public static void premain(String agentArgs, Instrumentation inst) {
ObjectSizeUtils.inst = inst;
} public static long sizeOf(Object o) throws IllegalAccessException {
if(inst == null) throw new IllegalStateException("not initialized yet");
SizeCounter sizeCounter = new SizeCounter();
sizeOf(o, sizeCounter);
return sizeCounter.getSize();
} public static void sizeOf(Object o,SizeCounter sizeCounter) throws IllegalAccessException {
if(o!=null){
sizeCounter.addSize(inst.getObjectSize(o));
Class c = o.getClass();
Field[] fields = c.getDeclaredFields();
for (Field f : fields) {
int mod = f.getModifiers();
if(Modifier.isStatic(mod) || f.getType().isPrimitive()){
continue;
}
f.setAccessible(true);
Object o1 = f.get(o);
sizeOf(o1,sizeCounter);
}
}
} } class SizeCounter{
private long size; public long getSize() {
return size;
} public void setSize(long size) {
this.size = size;
} public void addSize(long size){
this.setSize(this.getSize() + size);
}
}

这段代码并不完整,还有很多因素并没有考虑进去,大体思路差不多就是这样。

以下是agent jar文件的Manifest Attributes:
Premain-Class 
如果 JVM 启动时指定了代理,那么此属性指定代理类,即包含 premain 方法的类。如果 JVM 启动时指定了代理,那么此属性是必需的。如果该属性不存在,那么 JVM 将中止。注:此属性是类名,不是文件名或路径。

Agent-Class 
如果实现支持 VM 启动之后某一时刻启动代理的机制,那么此属性指定代理类。 即包含 agentmain 方法的类。 此属性是必需的,如果不存在,代理将无法启动。 注:这是类名,而不是文件名或路径。

Boot-Class-Path 
由引导类加载器搜索的路径列表。路径表示目录或库(在许多平台上通常作为 JAR 或 zip 库被引用)。查找类的特定于平台的机制失败后,引导类加载器会搜索这些路径。按列出的顺序搜索路径。列表中的路径由一个或多个空格分开。路径使用分层 URI 的路径组件语法。如果该路径以斜杠字符(“/”)开头,则为绝对路径,否则为相对路径。相对路径根据代理 JAR 文件的绝对路径解析。忽略格式不正确的路径和不存在的路径。如果代理是在 VM 启动之后某一时刻启动的,则忽略不表示 JAR 文件的路径。此属性是可选的。

Can-Redefine-Classes 
布尔值(true 或 false,与大小写无关)。是否能重定义此代理所需的类。true 以外的值均被视为 false。此属性是可选的,默认值为 false。

Can-Retransform-Classes 
布尔值(true 或 false,与大小写无关)。是否能重转换此代理所需的类。true 以外的值均被视为 false。此属性是可选的,默认值为 false。

Can-Set-Native-Method-Prefix 
布尔值(true 或 false,与大小写无关)。是否能设置此代理所需的本机方法前缀。true 以外的值均被视为 false。此属性是可选的,默认值为 false

Java - Instrumentation的更多相关文章

  1. java instrumentation &JVMTI

    Java Instrumentation (参考:http://www.ibm.com/developerworks/cn/java/j-lo-jse61/) 简介: 使用Instrumentatio ...

  2. 曹工说Spring Boot源码(25)-- Spring注解扫描的瑞士军刀,ASM + Java Instrumentation,顺便提提Jar包破解

    写在前面的话 相关背景及资源: 曹工说Spring Boot源码(1)-- Bean Definition到底是什么,附spring思维导图分享 曹工说Spring Boot源码(2)-- Bean ...

  3. Java Instrumentation插桩技术学习

    Instrumentation基础 openrasp中用到了Instrumentation技术,它的最大作用,就是类的动态改变和操作. 使用Instrumentation实际上也可以可以开发一个代理来 ...

  4. java高级-动态注入替换类Instrumentation

    介绍 利用java.lang.instrument(容器类) 做动态 Instrumentation(执行容器) 是 Java SE 5 的新特性. 使用 Instrumentation,开发者可以构 ...

  5. Java 中的纤程库 – Quasar

    来源:鸟窝, colobu.com/2016/07/14/Java-Fiber-Quasar/ 如有好文章投稿,请点击 → 这里了解详情 最近遇到的一个问题大概是微服务架构中经常会遇到的一个问题: 服 ...

  6. java基础-01基本概念

    java的特点 跨平台 所谓的平台,我们可以理解为操作系统. 大部分语言是不能跨平台的,比如c语言的程序在windows和linux上需要编写不同的代码. java程序是运行在JVM(Java Vir ...

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

    Java 字节码相关 字节码编程,也就是动态修改或是动态生成 Java 字节码.Java 的字节码相当于汇编,其中的一些细节. Java Zone: Introduction to Java Byte ...

  8. 不修改源代码,动态注入Java代码的方法(转)

    转自:https://blog.csdn.net/hiphoon_sun/article/details/38707927 有时,我们需要在不修改源代码的前提下往一个第三方的JAVA程序里注入自己的代 ...

  9. java agent技术原理及简单实现

    注:本文定义-在函数执行前后增加对应的逻辑的操作统称为MOCK 1.引子 在某天与QA同学进行沟通时,发现QA同学有针对某个方法调用时,有让该方法停止一段时间的需求,我对这部分的功能实现非常好奇,因此 ...

随机推荐

  1. 图片验证码demo示例

    1.首先我们需要一个生成图片验证码图片的一个工具类(下方会有代码示例) 代码如下: package com.util; import java.awt.BasicStroke; import java ...

  2. Java IO学习--(五)字节和字符数组

    内容列表 从InputStream或者Reader中读入数组 从OutputStream或者Writer中写数组 在java中常用字节和字符数组在应用中临时存储数据.而这些数组又是通常的数据读取来源或 ...

  3. C++笔记018:构造函数的调用规则

      原创笔记,转载请注明出处! 点击[关注],关注也是一种美德~ 一.默认构造函数 两个特殊的构造函数 1.默认无参构造函数 当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空 ...

  4. 大数据小视角1:从行存储到RCFile

    前段时间一直在忙碌写毕设与项目的事情,很久没有写一些学习心得与工作记录了,开了一个新的坑,希望能继续坚持写作与记录分布式存储相关的知识.为什么叫小视角呢?因为属于随想型的内容,可能一个由小的视角来审视 ...

  5. H5之postMessage 。实现跨域

    对于跨域我们有很多的解决方案,今天我来分享一下postMessage的那点事,postMessage是html5新增的一个解决跨域的一个方法,不过很可惜万恶的ie6,7不支持 postMessage( ...

  6. Mybatis 系列5

    上篇系列4中 为大家介绍了mybatis中别名的使用,以及其源码.本篇将为大家介绍TypeHandler, 并简单分析其源码. Mybatis中的TypeHandler是什么? 无论是 MyBatis ...

  7. json.parseArray源码解析

    json.parseArray源码解析 public static <T> List<T> parseArray(String text, Class<T> cla ...

  8. pc端页面打包成安卓apk

    一.phoneGap PhoneGap是一个采用HTML,CSS和JavaScript的技术,创建移动跨平台移动应用程序的快速开发平台.它使开发者能够在网页中调用IOS,Android,Palm,Sy ...

  9. PHP实现单例模式和观察者模式

    单例模式的实现: PHP中单例模式常用在数据库连接部分,省掉了大量的new操作进而节省了很多资源.单例模式还可以用在全局配置类中. 单例模式,顾名思义就是说只有一个实例,这就要求防止外部随意实例化对象 ...

  10. Java虚拟机-内存tips

    java虚拟机内存可以分为独占区和共享区. 独占区:虚拟内存栈.本地方法栈.程序计数器. 共享区:方法区.Java堆(用来存放对象实例). 程序计数器 比较小的内存空间,当前线程所执行的字节码的行号指 ...