代码之家  ›  专栏  ›  技术社区  ›  pts

如何增加Java堆栈的大小?

  •  105
  • pts  · 技术社区  · 14 年前

    最初,我想增加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));
      }
    }
    

    java -Xss... 具有足够大值的命令行标志。为了这个计划 TT 上面,它在OpenJDK的JVM中是这样工作的:

    $ 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)
    • fact(1 << 18)
    • -Xss9m足以 fact(1 << 19)
    • fact(1 << 20)
    • -Xss35m足以 fact(1 << 21)
    • -Xss68m足以 fact(1 << 22)
    • fact(1 << 23)
    • -Xss258m足以 fact(1 << 24)
    • -Xss515m足以 fact(1 << 25)

    从上面的数字来看,Java似乎在为上面的函数使用每个堆栈帧大约16个字节,这是合理的。

    上面的枚举包含 而不是 够了吗 ,因为堆栈需求是不确定的:使用相同的源文件和相同的 -Xss... 有时成功,有时失败 . 例如,对于1<<20, -Xss18m 10次中有7次就足够了 -Xss19m 也不总是足够,但是 -Xss20m 已经足够了(总共100次)。是垃圾收集、JIT启动还是其他原因导致了这种不确定性行为?

    一次打印的堆叠痕迹 (可能还有其他异常)只显示运行时堆栈中最新的1024个元素。下面的答案演示了如何计算到达的确切深度(可能比1024大得多)。

    许多对此作出回应的人指出,考虑同一算法的替代性、不太需要堆栈的实现是一种好的、安全的编码实践。一般来说,可以将一组递归函数转换为迭代函数(使用。 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));
      }
    }
    

    仅供参考,正如上面的迭代解所示 事实 long 会溢出。重构 事实 所以它会返回一个 BigInteger 而不是 长的 对于大的输入也会产生精确的结果。

    9 回复  |  直到 10 年前
        1
  •  82
  •   pts    14 年前

    隐马尔可夫模型。。。它适用于我,堆栈远小于999MB:

    > java -Xss4m Test
    0
    

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

        2
  •  11
  •   Peter Mortensen icecrime    7 年前

    我假设您通过堆栈跟踪中的循环行来计算“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);
        }
    }
    
        3
  •  9
  •   Community CDub    7 年前

    如果您想使用线程堆栈大小,您需要查看热点JVM上的-Xss选项。在非热点VM上可能会有所不同,因为JVM的-X参数是特定于分布的IIRC。

    在热点上,这看起来像 java -Xss16M 如果你想做16毫克的。

    类型 java -X -help

    Does the JVM prevent tail call optimizations? ). 尝试重构上面的阶乘代码,以使用while循环而不是递归方法调用。

        4
  •  8
  •   Peter Mortensen icecrime    7 年前

    控制进程内堆栈大小的唯一方法是启动一个新进程 Thread -Xss 参数。

    public class TT {
        private static int level = 0;
    
        public static long fact(int n) {
            level++;
            return n < 2 ? n : n * fact(n - 1);
        }
    
        public static void main(String[] args) throws InterruptedException {
            Thread t = new Thread(null, null, "TT", 1000000) {
                @Override
                public void run() {
                    try {
                        level = 0;
                        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);
                    }
                }
    
            };
            t.start();
            t.join();
            try {
                level = 0;
                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);
            }
        }
    
    }
    
        5
  •  3
  •   Alper t. Turker    6 年前

    添加此选项

    --driver-java-options -Xss512m
    

        6
  •  2
  •   Peter Lawrey    14 年前

    注意:使用-Xss设置每个线程的堆栈大小,这是一个非常糟糕的主意。

    另一种方法是字节码操作,改变代码如下;

    public static long fact(int n) { 
        return n < 2 ? n : n > 127 ? 0 : n * fact(n - 1); 
    }
    

    如果n>127的每个答案都是0。这样可以避免更改源代码。

        7
  •  1
  •   Val    10 年前

    我做的 Anagram excersize ,就像 Count Change 有问题,但有5万面额(硬币)。我是 not sure that it can be done iteratively

      def measureStackDepth(ss: Long): Long = {
        var depth: Long = 0
          val thread: Thread = new Thread(null, new Runnable() {
            override def run() {
              try {
              def sum(n: Long): Long = {depth += 1; if (n== 0) 0 else sum(n-1) + 1}
              println("fact = " + sum(ss * 10))
              } catch {
                case e: StackOverflowError => // eat the exception, that is expected
              }
            }
          }, "deep stack for money exchange", ss)
          thread.start()
          thread.join()
        depth
      }                                               //> measureStackDepth: (ss: Long)Long
    
    
      for (ss <- (0 to 10)) println("ss = 10^" +  ss + " allows stack of size " -> measureStackDepth((scala.math.pow (10, ss)).toLong) )
                                                      //> fact = 10
                                                      //| (ss = 10^0 allows stack of size ,11)
                                                      //| fact = 100
                                                      //| (ss = 10^1 allows stack of size ,101)
                                                      //| fact = 1000
                                                      //| (ss = 10^2 allows stack of size ,1001)
                                                      //| fact = 10000
                                                      //| (ss = 10^3 allows stack of size ,10001)
                                                      //| (ss = 10^4 allows stack of size ,1336)
                                                      //| (ss = 10^5 allows stack of size ,5456)
                                                      //| (ss = 10^6 allows stack of size ,62736)
                                                      //| (ss = 10^7 allows stack of size ,623876)
                                                      //| (ss = 10^8 allows stack of size ,6247732)
                                                      //| (ss = 10^9 allows stack of size ,62498160)
    

    您可以看到,当向线程分配更多的堆栈时,堆栈可以指数级地增长得更深。

        8
  •  0
  •   helios    14 年前

    1<<15深度的递归

    我建议不要尝试。堆栈的大小将是 2^15 * sizeof(stack-frame) . 我不知道堆栈帧大小是多少,但2^15是32.768。差不多。。。好吧,如果它停在1024(2^10),你必须使它大2^5倍,它是,32倍于你的实际设置。

        9
  •  0
  •   abscondment    14 年前

    看看这篇文章,它对函数和代码进行了一些分析:

    http://threebrothers.org/brendan/blog/stirlings-approximation-formula-clojure/