为什么这个简单的Java字节码会导致StackOverflow错误?

时间:2011-01-19 02:35:00

标签: java java-native-interface bytecode instrumentation stack-overflow

我需要检测本机方法,以便在正常执行之前进行简单的静态调用。因为这些方法是原生的,所以我必须使用"setNativePrefix" feature并使用原始方法签名对本机方法进行中间调用。

在我认为是一个简单的字节码更改来实现这一点之后,即使堆栈基本上是空的,我也会在执行包装器方法之前得到一个StackOverflowError。这是我的测试类:

public class SimpleTest {
    public static void main(String[] args) throws IOException {
        Perf.getPerf().highResCounter();
    }
}

通常,该程序在控制台上不会产生任何结果。但是,我的检测字节码在执行本机方法$ wrapper $ highResCounter()之前执行println()。这可以在检测后的相关Perf类字节码中看到:

public long highResCounter() {
    getstatic PrintStream System.out
    ldc String Constant "this is an instrumented println"
    invokevirtual void PrintStream.println(String)
    aload 0
    invokevirtual long Perf.$wrapped$highResCounter()
    lreturn
}

public native long $wrapped$highResCounter();

我是Java字节码的新手,所以我在这里犯了一个错误。这是程序的输出,它显示println()被执行,但在第一个之后的某个地方 invokevirtual 调用抛出StackOverflowError:

this is an instrumented println call
Exception in thread "main" java.lang.StackOverflowError
   at com.foo.SimpleTest.main(SimpleTest.java:17)

可能导致此StackOverflowError的原因是什么?我该如何解决?

3 个答案:

答案 0 :(得分:1)

你的highResCounter方法正在调用自己:

public long highResCounter() {
    [...]
    invokevirtual long Perf.$wrapped$highResCounter()

您是否有更多代码可以向我们展示以找出原因?

答案 1 :(得分:1)

发布的代码中没有错误。问题是由MAX_STACK和MAX_LOCALS方法属性的无效值引起的,这些属性通常由编译器计算。

我正在使用的字节码库ASM让您有机会自动计算这些值,而我没有利用它。通过将ASM ClassWriter构造函数更改为以下内容,我得到无错误的代码:

int flags = ClassWriter.COMPUTE_FRAMES | ClassWriter.COMPUTE_MAXS;
ClassWriter writer = new ClassWriter(flags);

[1] http://asm.ow2.org/asm33/javadoc/user/org/objectweb/asm/ClassWriter.html#COMPUTE_FRAMES

答案 2 :(得分:0)

显示本机功能,它本身也可能是一个问题。 iirc StackOverFlowError被捕获,因此即使是JNI C代码也可能导致它。