如何增加Java堆栈大小?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3700459/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-14 03:38:29  来源:igfitidea点击:

How to increase the Java stack size?

javastackstack-overflow

提问by pts

I asked this question to get to know how to increase the runtime call stack size in the JVM. I've got an answer to this, and I've also got many useful answers and comments relevant to how Java handles the situation where a large runtime stack is needed. I've extended my question with the summary of the responses.

我问这个问题是为了了解如何增加 JVM 中的运行时调用堆栈大小。我已经得到了答案,而且我也得到了许多与 Java 如何处理需要大型运行时堆栈的情况相关的有用答案和评论。我已经用答复摘要扩展了我的问题。

Originally I wanted to increase the JVM stack size so programs like runs without a StackOverflowError.

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

The corresponding configuration setting is the java -Xss...command-line flag with a large enough value. For the program TTabove, it works like this with OpenJDK's JVM:

相应的配置设置是java -Xss...具有足够大值的命令行标志。对于TT上面的程序,它在 OpenJDK 的 JVM 中是这样工作的:

$ javac TT.java
$ java -Xss4m TT

One of the answers has also pointed out that the -X...flags are implementation dependent. I was using

答案之一还指出-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)

It is also possible to specify a large stack only for one thread (see in one of the answers how). This is recommended over java -Xss...to avoid wasting memory for threads that don't need it.

也可以只为一个线程指定一个大堆栈(参见答案之一)。建议java -Xss...这样做以避免为不需要它的线程浪费内存。

I was curious how large a stack the program above exactly needs, so I've run it nincreased:

我很好奇上面的程序到底需要多大的堆栈,所以我n增加了它:

  • -Xss4m can be enough for fact(1 << 15)
  • -Xss5m can be enough for fact(1 << 17)
  • -Xss7m can be enough for fact(1 << 18)
  • -Xss9m can be enough for fact(1 << 19)
  • -Xss18m can be enough for fact(1 << 20)
  • -Xss35m can be enough for fact(1 << 21)
  • -Xss68m can be enough for fact(1 << 22)
  • -Xss129m can be enough for fact(1 << 23)
  • -Xss258m can be enough for fact(1 << 24)
  • -Xss515m can be enough for fact(1 << 25)
  • -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)

From the numbers above it seems that Java is using about 16 bytes per stack frame for the function above, which is reasonable.

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

The enumeration above contains can be enoughinstead of is enough, because the stack requirement is not deterministic: running it multiple times with the same source file and the same -Xss...sometimes succeeds and sometimes yields a StackOverflowError. E.g. for 1 << 20, -Xss18mwas enough in 7 runs out of 10, and -Xss19mwasn't always enough either, but -Xss20mwas enough (in all 100 runs out of 100). Does garbage collection, the JIT kicking in, or something else cause this nondeterministic behavior?

上面的枚举 contains can be enough而不是is enough,因为堆栈要求不是确定性的:使用相同的源文件多次运行它-Xss...,有时会成功,有时会产生StackOverflowError. 例如,对于 1 << 20,-Xss18m在 10 次中的 7 次中就足够了,-Xss19m也并不总是足够,但-Xss20m已经足够了(在 100 次中的所有 100 次中)。垃圾收集、JIT 启动或其他原因是否会导致这种不确定性行为?

The stack trace printed at a StackOverflowError(and possibly at other exceptions as well) shows only the most recent 1024 elements of the runtime stack. An answer below demonstrates how to count the exact depth reached (which might be a lot larger than 1024).

在 a 处StackOverflowError(也可能在其他异常处)打印的堆栈跟踪仅显示运行时堆栈的最新 1024 个元素。下面的答案演示了如何计算达到的确切深度(可能比 1024 大很多)。

Many people who responded has pointed out that it is a good and safe coding practice to consider alternative, less stack-hungry implementations of the same algorithm. In general, it is possible to convert to a set of recursive functions to iterative functions (using a e.g. Stackobject, which gets populated on the heap instead of on the runtime stack). For this particular factfunction, it is quite easy to convert it. My iterative version would look like:

许多回应的人指出,考虑相同算法的替代性的、不那么需要堆栈的实现是一种良好且安全的编码实践。通常,可以将一组递归函数转换为迭代函数(使用例如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));
  }
}

FYI, as the iterative solution above shows it, the factfunction cannot compute the exact factorial of numbers above 65 (actually, even above 20), because the Java built-in type longwould overflow. Refactoring factso it would return a BigIntegerinstead of longwould yield exact results for large inputs as well.

仅供参考,正如上面的迭代解决方案所示,该fact函数无法计算大于 65(实际上,甚至大于 20)的数字的精确阶乘,因为 Java 内置类型long会溢出。重构fact以便它返回 aBigInteger而不是long将为大输入产生精确的结果。

采纳答案by Jon Skeet

Hmm... it works for me and with far less than 999MB of stack:

嗯……它对我有用,而且堆栈远远少于 999MB:

> java -Xss4m Test
0

(Windows JDK 7, build 17.0-b05 client VM, and Linux JDK 6 - same version information as you posted)

(Windows JDK 7、构建 17.0-b05 客户端 VM 和 Linux JDK 6 - 与您发布的版本信息相同)

回答by whaley

If you want to play with the thread stack size, you'll want to look at the -Xss option on the Hotspot JVM. It may be something different on non Hotspot VM's since the -X parameters to the JVM are distribution specific, IIRC.

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

On Hotspot, this looks like java -Xss16Mif you want to make the size 16 megs.

在 Hotspot 上,java -Xss16M如果您想制作 16 兆的大小,这看起来像。

Type java -X -helpif you want to see all of the distribution specific JVM parameters you can pass in. I am not sure if this works the same on other JVMs, but it prints all of Hotspot specific parameters.

java -X -help如果您想查看您可以传入的所有特定于发行版的 JVM 参数,请键入。我不确定这在其他 JVM 上是否相同,但它会打印所有 Hotspot 特定参数。

For what it's worth - I would recommend limiting your use of recursive methods in Java. It's not too great at optimizing them - for one the JVM doesn't support tail recursion (see Does the JVM prevent tail call optimizations?). Try refactoring your factorial code above to use a while loop instead of recursive method calls.

对于它的价值 - 我建议限制您在 Java 中使用递归方法。优化它们并不是太好 - 一方面,JVM 不支持尾递归(请参阅JVM 是否阻止尾调用优化?)。尝试重构上面的阶乘代码以使用 while 循环而不是递归方法调用。

回答by Jay

I assume you calculated the "depth of 1024" by the recurring lines in the stack trace?

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

Obviously, the stack trace array length in Throwable seems to be limited to 1024. Try the following program:

显然,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);
    }
}

回答by helios

Weird! You are saying that you want to generate a recursion of 1<<15 depth???!!!!

奇怪的!您是说要生成1<<15 深度递归???!!!!!!

I'd suggest DON'T try it. The size of the stack will be 2^15 * sizeof(stack-frame). I don't know what stack-frame size is, but 2^15 is 32.768. Pretty much... Well, if it stops at 1024 (2^10) you'll have to make it 2^5 times bigger, it is, 32 times bigger than with your actual setting.

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

回答by Dennis C

The only way to control the size of stack within process is start a new Thread. But you can also control by creating a self-calling sub Java process with the -Xssparameter.

控制进程内堆栈大小的唯一方法是启动一个新的Thread. 但是你也可以通过创建一个带-Xss参数的自调用子Java进程来控制。

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);
        }
    }

}

回答by Peter Lawrey

It is hard to give a sensible solution since you are keen to avoid all sane approaches. Refactoring one line of code is the senible solution.

由于您热衷于避免所有理智的方法,因此很难给出合理的解决方案。重构一行代码是明智的解决方案。

Note: Using -Xss sets the stack size of every thread and is a very bad idea.

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

Another approach is byte code manipulation to change the code as follows;

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

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

given every answer for n > 127 is 0. This avoid changing the source code.

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

回答by abscondment

Other posters have pointed out how to increase memory and that you could memoize calls. I'd suggest that for many applications, you can use Stirling's formula to approximate large n! very quickly with almost no memory footprint.

其他海报指出了如何增加内存以及您可以记住通话。我建议对于许多应用程序,您可以使用斯特林公式来近似大 n!非常快,几乎没有内存占用。

Take a gander at this post, which has some analysis of the function and code:

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

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

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

回答by Val

I did Anagram excersize, which is like Count Changeproblem but with 50 000 denominations (coins). I am not sure that it can be done iteratively, I do not care. I just know that the -xss option had no effect -- I always failed after 1024 stack frames (might be scala does bad job delivering to to java or printStackTrace limitation. I do not know). This is bad option, as explained anyway. You do not want all threads in to app to be monstrous. However, I did some experiments with new Thread (stack size). This works indeed,

我做了Anagram excersize,这就像Count Change问题,但有 50 000 个面额(硬币)。我不确定它是否可以迭代完成,我不在乎。我只知道 -xss 选项没有效果——我总是在 1024 个堆栈帧后失败(可能是 scala 无法将传递到 java 或 printStackTrace 限制。我不知道)。无论如何,这是一个糟糕的选择。您不希望应用程序中的所有线程都是可怕的。但是,我对新线程(堆栈大小)做了一些实验。这确实有效,

  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)

You see that stack can grow exponentially deeper with exponentially more stack alloted to the thread.

您会看到,随着分配给线程的堆栈呈指数级增长,堆栈可以呈指数级增长。

回答by Guibin Zhang

Add this option

添加此选项

--driver-java-options -Xss512m

to your spark-submit command will fix this issue.

到您的 spark-submit 命令将解决此问题。