How to increase the Java stack size?(如何增加 Java 堆栈大小?)
问题描述
我问这个问题是为了了解如何增加 JVM 中的运行时调用堆栈大小.我已经得到了答案,而且我也得到了许多有用的答案和评论,这些答案和评论与 Java 如何处理需要大型运行时堆栈的情况有关.我已经通过回答摘要扩展了我的问题.
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 堆栈大小,这样程序就可以在没有 StackOverflowError
的情况下运行.
Originally I wanted to increase the JVM stack size so programs like runs without a 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 上是这样工作的:
The corresponding configuration setting is the java -Xss...
command-line flag with a large enough value. For the program TT
above, it works like this with OpenJDK's JVM:
$ javac TT.java
$ java -Xss4m TT
其中一个答案还指出 -X...
标志是依赖于实现的.我正在使用
One of the answers has also pointed out that the -X...
flags are implementation dependent. I was using
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...
上这样做,以避免为不需要它的线程浪费内存.
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.
我很好奇上面的程序到底需要多大的堆栈,所以我运行它 n
增加了:
I was curious how large a stack the program above exactly needs, so I've run it n
increased:
- -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 个字节,这是合理的.
From the numbers above it seems that Java is using about 16 bytes per stack frame for the function above, which is reasonable.
上面的枚举包含 can be enough 而不是 is enough,因为堆栈要求不是确定性的:使用相同的源文件和相同的 -Xss...
有时会成功,有时会产生 StackOverflowError
.例如.对于 1 <<20, -Xss18m
在 7 次运行中 10 次就足够了,并且 -Xss19m
也并不总是足够,但是 -Xss20m
就足够了(总共有 100 个用完 100 个).垃圾收集、JIT 启动或其他原因是否会导致这种不确定性行为?
The enumeration above contains can be enough instead 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, -Xss18m
was enough in 7 runs out of 10, and -Xss19m
wasn't always enough either, but -Xss20m
was enough (in all 100 runs out of 100). Does garbage collection, the JIT kicking in, or something else cause this nondeterministic behavior?
在 StackOverflowError
处打印的堆栈跟踪(也可能在其他异常处)仅显示运行时堆栈的最新 1024 个元素.下面的答案演示了如何计算达到的确切深度(可能比 1024 大很多).
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).
许多回应的人指出,考虑相同算法的替代、更少的堆栈消耗实现是一种良好且安全的编码实践.通常,可以将一组递归函数转换为迭代函数(使用例如 Stack
对象,该对象填充在堆上而不是运行时堆栈上).对于这个特殊的 fact
函数,转换它非常容易.我的迭代版本如下所示:
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. Stack
object, which gets populated on the heap instead of on the runtime stack). For this particular fact
function, it is quite easy to convert it. My iterative version would look like:
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));
}
}
仅供参考,正如上面的迭代解决方案所示,fact
函数无法计算 65 以上(实际上,甚至超过 20)的数字的确切阶乘,因为 Java 内置类型 long
会溢出.重构 fact
使其返回 BigInteger
而不是 long
也会为大型输入产生准确的结果.
FYI, as the iterative solution above shows it, the fact
function cannot compute the exact factorial of numbers above 65 (actually, even above 20), because the Java built-in type long
would overflow. Refactoring fact
so it would return a BigInteger
instead of long
would yield exact results for large inputs as well.
推荐答案
嗯...它对我有用,而且堆栈远小于 999MB:
Hmm... it works for me and with far less than 999MB of stack:
> java -Xss4m Test
0
(Windows JDK 7,构建 17.0-b05 客户端 VM 和 Linux JDK 6 - 与您发布的版本信息相同)
(Windows JDK 7, build 17.0-b05 client VM, and Linux JDK 6 - same version information as you posted)
这篇关于如何增加 Java 堆栈大小?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持编程学习网!
本文标题为:如何增加 Java 堆栈大小?
基础教程推荐
- Java 中保存最后 N 个元素的大小受限队列 2022-01-01
- 如何对 HashSet 进行排序? 2022-01-01
- 在螺旋中写一个字符串 2022-01-01
- 首次使用 Hadoop,MapReduce Job 不运行 Reduce Phase 2022-01-01
- Spring Boot Freemarker从2.2.0升级失败 2022-01-01
- 如何使用 Eclipse 检查调试符号状态? 2022-01-01
- 由于对所需库 rt.jar 的限制,对类的访问限制? 2022-01-01
- 如何使用 Stream 在集合中拆分奇数和偶数以及两者的总和 2022-01-01
- 如何在不安装整个 WTP 包的情况下将 Tomcat 8 添加到 Eclipse Kepler 2022-01-01
- 如何强制对超级方法进行多态调用? 2022-01-01