JVM动态调整字节码

粗略的点开btrace的源码看了一下,实际上他只是封装了JDK自带的功能而已

1. attach client到java进程

 VirtualMachine vm = null;
            if (debug) {
                debugPrint("attaching to " + pid);
            }
            vm = VirtualMachine.attach(pid);
            if (debug) {
                debugPrint("checking port availability: " + port);
            }
  Properties serverVmProps = vm.getSystemProperties();
            int serverPort = Integer.parseInt(serverVmProps.getProperty("btrace.port", "-1"));
            if (serverPort != -1) {
                if (serverPort != port) {
                    throw new IOException("Can not attach to PID " + pid + " on port " + port + ". There is already a BTrace server active on port " + serverPort + "!");
                }
            } else {
                if (!isPortAvailable(port)) {
                    throw new IOException("Port " + port + " unavailable.");
                }
            }

vm.loadAgent(agentPath, agentArgs);

 

2. 修改字节码

获取 Instrumentation 接口的实例有两种方式: 

当 JVM 以指示一个代理类的方式启动时,将传递给代理类的 premain 方法一个 Instrumentation 实例。 

当 JVM 提供某种机制在 JVM 启动之后某一时刻启动代理时,将传递给代理代码的 agentmain 方法一个 Instrumentation 实例。

拿到Instrumentation实例后,就可以替换class字节码了

// 返回 JVM 当前加载的所有类的数组
            for (Class c : inst.getAllLoadedClasses()) {
                if (c != null) {
                    cc.get(c);
                    if (inst.isModifiableClass(c) &&  isCandidate(c)) {
                        debugPrint("candidate " + c + " added");
                        list.add(c);
                    }
                }
            }
            list.trimToSize();
            int size = list.size();
            if (size > 0) {
                Class[] classes = new Class[size];
                list.toArray(classes);
                startRetransformClasses(size);
                if (isDebug()) {
                    for(Class c : classes) {
                        try {
                            // 如果重转换的方法有活动的堆栈帧,那么这些活动的帧将继续运行原方法的字节码。重转换的方法将用于新的调用
                            // 此方法不会引起任何初始化操作,JVM 惯例语义下发生的初始化除外。换句话说,重定义一个类不会引起其初始化方法的运行。静态变量的值将与调用之前的值一样。
                            inst.retransformClasses(c);
                        } catch (VerifyError e) {
                            debugPrint("verification error: " + c.getName());
                        }
                    }
                } else {
                    inst.retransformClasses(classes);
                }
            }

猜你喜欢

转载自jimmee.iteye.com/blog/2291360