我们可以在加载后重新转换类吗?

时间:2016-08-25 08:21:46

标签: java instrumentation javaagents

我们目前使用premain在加载之前转换所有类。 但我们希望控制变换,这意味着我们可以删除/重新添加我们在方法中插入的代码。

所以我们发现了转换,但我不太明白。

  1. 我们可以重新转换加载的类吗?
  2. 如果可以,重新转换的类的实例是否会运行重新转换的代码?
  3. 如果无法完成,是否有任何技术可以在运行时更改某些指定方法的代码。
  4. 已更新

    例如,我们有A类.A类有两个方法foo()和bar():

    void foo() {
        while(true) {
            bar();
        }
    }
    void bar() {
        System.out.println("a");
    }
    

    我们称之为Instrument.retransformClasses(A.class)。并将bar()更改为:

    void bar() {
        System.out.println("b");
    }
    

    如果有一个线程已经调用了foo(),我们可以得到输出:

    ...
    a
    a
    a
    b
    b
    b
    ...
    

    如果没有,有没有办法实现它?

1 个答案:

答案 0 :(得分:1)

所以你已经有一个ClassFileTransformer来改变一个班级'字节码在加载并安装到JVM之前。现在你想要在JVM生命周期中再次重新转换,不是吗?

嗯,转换可以通过Instrumentation,通过Instrumentation.retransformClasses方法完成。但首先,您必须通过调用Instrumentation.isRetransformClassesSupported来确保支持重新转换。

这次重新转换何时生效?根据javadocs:

  

如果重新传输的方法具有活动堆栈帧,那些活动帧将继续运行原始方法的字节码。重新传输的方法将用于新的调用。

这意味着,在您的示例中,foo()会反复调用bar()。如果同时重新转换bar,则转换后的代码将生效调用bar 的下一次迭代。

我将为您的示例添加更多复杂性:

void foo() {
    while(true) {
        bar();
    }
}

void bar() {
    myRetransform();
    System.out.println("a");
}

void myRetransform() {
    // This is where a retransformation of class A, method bar, is triggered.
}

当触发重新转换时,当前堆栈帧为:

  • myRetransform
  • FOO

有一个带有bar的有效调用堆栈框架,因此当myRetransform返回时,它仍然会打印一个" a"在system.out。

然后,bar将返回并保留当前的堆栈帧:

  • FOO

现在是最后一次触发转换生效的时间。即:在下次调用bar时。