zoukankan      html  css  js  c++  java
  • Java

    使用JRebel启动工程时加上VM参数时有一个参数是"-javaagent:D:jrebel_5.6.0jrebel.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。

  • 相关阅读:
    POJ1769 Minimizing maximizer(DP + 线段树)
    ZOJ3201 Tree of Tree(树形DP)
    POJ3613 Cow Relays(矩阵快速幂)
    POJ3635 Full Tank?(DP + Dijkstra)
    ZOJ3195 Design the city(LCA)
    POJ3368 Frequent values(RMQ线段树)
    POJ3686 The Windy's(最小费用最大流)
    HDU4871 Shortest-path tree(最短路径树 + 树的点分治)
    POJ3013 Big Christmas Tree(最短路径树)
    Gym100685G Gadget Hackwrench(倍增LCA)
  • 原文地址:https://www.cnblogs.com/kavlez/p/4074660.html
Copyright © 2011-2022 走看看