我在实际项目中遇到了StackOverflowerr,并制作了一个简单的模型来说明这个问题。它是一个测试类,它调用一些递归方法并保存错误的深度。
public class Main {
static int c = 0;
public static void main(String[] args) {
long sum = 0;
int exps = 100;
for (int i = 0; i < exps; ++i) {
c = 0;
try {
simpleRecursion();
} catch (StackOverflowError e) {
sum += c;
}
}
System.out.println("Average method call depth: " + (sum / exps));
}
public static void simpleRecursion() {
simpleMethod();
++c;
simpleRecursion();
}
}
simplemethod有两个版本:
public static void simpleMethod() {
}
它在测试中获取51k或59k方法调用的深度。
public static void simpleMethod() {
c += 0;
}
它在测试中获取48k或58k方法调用的深度。
为什么这些认识得到了不同的结果?我不明白在第二种情况下堆栈中有什么额外的数据。我认为simplemethod不应该影响堆栈内存,因为它不在调用链中。
1条答案
按热度按时间kyxcudwk1#
由于性能原因,您遇到的问题可能与jvm内联方法有关。内联方法可能会影响为该方法分配的堆栈大小。你可以和我核对一下
javap -v
一个方法的堆栈大小有多大,调用该方法时会分配堆栈大小。对于你的代码javap -v
具体如下:这个
simpleRecursion()
方法:这个
simpleMethod()
不带c+=0;
生产线:这个
simpleMethod();
方法与c+=0;
生产线:具有空主体的方法变量要求堆栈大小为
0
,其中方法随c+=0;
行要求堆栈大小为2
.我猜当方法
simpleMethod()
内联到simpleRecursion()
通过jvm/jit/hotspot(参见其他问题,如java中是否有内联函数?或者java内联方法会在优化过程中使用吗?)它会增加simpleRecursion()
为所需的额外堆栈大小腾出空间simpleMethod()
. 现在的堆栈大小simpleRecursion()
是更大的,这会导致用一个StackOverflowError
早期的。不幸的是,我无法验证这一点,因为涉及jit/热点。见鬼,即使多次运行同一个应用程序也会导致
c
最后。当我试着用simpleRecursion()
变体c+=0;
而不是方法调用simpleMethod();
,堆栈大小保持不变,很可能是因为编译器足够聪明,可以使用相同的堆栈大小2
.