如何增加 Java 堆栈大小?
我问这个问题是为了了解如何增加 JVM 中的运行时调用堆栈大小。我有一个答案,我也得到了许多有用的答案和评论,这些答案和评论与Java如何处理需要大型运行时堆栈的情况有关。我用回答的摘要扩展了我的问题。
最初我想增加JVM堆栈的大小,以便程序像在没有.StackOverflowError
public class TT {
public static long fact(int n) {
return n < 2 ? 1 : n * fact(n - 1);
}
public static void main(String[] args) {
System.out.println(fact(1 << 15));
}
}
相应的配置设置是具有足够大值的命令行标志。对于上面的程序,它与OpenJDK的JVM一起工作:java -Xss...
TT
$ javac TT.java
$ java -Xss4m TT
其中一个答案还指出,这些标志取决于实现。我正在使用-X...
java version "1.6.0_18"
OpenJDK Runtime Environment (IcedTea6 1.8.1) (6b18-1.8.1-0ubuntu1~8.04.3)
OpenJDK 64-Bit Server VM (build 16.0-b13, mixed mode)
也可以仅为一个线程指定大型堆栈(请参阅其中一个答案)。建议这样做,以避免将内存浪费给不需要它的线程。java -Xss...
我很好奇上面的程序究竟需要多大的堆栈,所以我运行它增加了:n
- -Xss4m 可以足够
fact(1 << 15)
- -Xss5m 可以足够
fact(1 << 17)
- -Xss7m 可以足够
fact(1 << 18)
- -Xss9m 可以足够
fact(1 << 19)
- -Xss18m 可以足够
fact(1 << 20)
- -Xss35m 可以足够
fact(1 << 21)
- -Xss68m 可以足够
fact(1 << 22)
- -Xss129m 可以足够
fact(1 << 23)
- -Xss258m 可以足够
fact(1 << 24)
- -Xss515m 可以足够
fact(1 << 25)
从上面的数字来看,Java似乎对上面的函数使用每个堆栈帧大约16个字节,这是合理的。
上面的枚举 contains 就足够了,而不是足够了,因为堆栈要求不是确定性的:使用相同的源文件多次运行它,并且相同的文件有时成功,有时会产生 .例如,对于1<<20,在10次中有7次运行就足够了,并且并不总是足够,但足够了(在所有100次运行中,100次)。垃圾回收、JIT 启动或其他原因是否会导致这种非确定性行为?-Xss...
StackOverflowError
-Xss18m
-Xss19m
-Xss20m
在某个位置打印的堆栈跟踪(也可能在其他异常处打印)仅显示运行时堆栈的最新 1024 个元素。下面的答案演示了如何计算达到的确切深度(可能比1024大得多)。StackOverflowError
许多回应者指出,考虑同一算法的替代,堆栈饥渴较少的实现是一种良好且安全的编码实践。通常,可以将一组递归函数转换为迭代函数(例如 对象,它填充在堆上而不是运行时堆栈上)。对于此特定功能,转换它非常容易。我的迭代版本如下所示:Stack
fact
public class TTIterative {
public static long fact(int n) {
if (n < 2) return 1;
if (n > 65) return 0; // Enough powers of 2 in the product to make it (long)0.
long f = 2;
for (int i = 3; i <= n; ++i) {
f *= i;
}
return f;
}
public static void main(String[] args) {
System.out.println(fact(1 << 15));
}
}
仅供参考,如上面的迭代解决方案所示,该函数无法计算高于65的数字(实际上,甚至高于20)的确切阶乘,因为Java内置类型会溢出。重构,以便它返回 a 而不是,也会为大型输入产生精确的结果。fact
long
fact
BigInteger
long