如何增加 Java 堆栈大小?

2022-08-31 07:51:17

我问这个问题是为了了解如何增加 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

许多回应者指出,考虑同一算法的替代,堆栈饥渴较少的实现是一种良好且安全的编码实践。通常,可以将一组递归函数转换为迭代函数(例如 对象,它填充在堆上而不是运行时堆栈上)。对于此特定功能,转换它非常容易。我的迭代版本如下所示:Stackfact

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 而不是,也会为大型输入产生精确的结果。factlongfactBigIntegerlong


答案 1

嗯。。。它适用于我,并且堆栈远远少于999MB:

> java -Xss4m Test
0

(Windows JDK 7、build 17.0-b05 client VM 和 Linux JDK 6 - 与你发布的版本信息相同)


答案 2

我假设您通过堆栈跟踪中的重复行计算“1024的深度”?

显然,Throwable 中的堆栈跟踪数组长度似乎限制为 1024。请尝试以下程序:

public class Test {

    public static void main(String[] args) {

        try {
            System.out.println(fact(1 << 15));
        }
        catch (StackOverflowError e) {
            System.err.println("true recursion level was " + level);
            System.err.println("reported recursion level was " +
                               e.getStackTrace().length);
        }
    }

    private static int level = 0;
    public static long fact(int n) {
        level++;
        return n < 2 ? n : n * fact(n - 1);
    }
}