如何增加Java堆栈大小?
我问过这个问题,以了解如何增加JVM中的运行时调用堆栈大小。我已经得到了一个答案,并且我还得到了许多有用的答案和评论,这些答案和评论与Java如何处理需要大型运行时堆栈的情况相关。我已经回答了问题的总结。如何增加Java堆栈大小?
本来我想增加JVM堆栈的大小,所以像没有*Error
的程序运行。
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)
- -Xss7m可以
fact(1 << 18)
足够 - -Xss9m可以为
fact(1 << 19)
- -Xss18m可以连接足够ough为
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个字节对于上面的功能来说,这是合理的。
以上枚举包含可以足够代替足够,因为堆栈的要求是不确定性:运行它多次具有相同的源文件和相同-Xss...
有时成功并且有时产生一个*Error
。例如。对于1 < < 20,-Xss18m
已经足够用于10次中的7次,并且-Xss19m
也不总是足够的,但是-Xss20m
就足够了(在全部100次中100次)。垃圾收集,JIT踢,或其他事情导致这种非确定性行为?
打印在*Error
(可能还有其他例外)的堆栈跟踪仅显示运行时堆栈的最新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));
}
}
仅供参考,如上面的迭代求解显示它的fact
功能不能计算数字的65岁以上的确切因子(实际上,甚至高于20),因为Java内置类型long
会溢出。重构fact
所以它会返回一个BigInteger
代替long
会产生精确的结果大投入为好。
嗯......我的作品,并与远小于栈的999MB:
> java -Xss4m Test
0
(视窗JDK 7,建设17.0-B05的客户端虚拟机和Linux JDK 6 - 相同版本的信息,你发布)
很可能是我的评论,当我意识到Neil发布的同样的东西时,我删除了它。 – Sean 2010-09-13 14:37:03
感谢这个问题和你的回答,我设法完成了我的assigment。我的DFS函数必须在具有〜10^5个顶点的图上递归。最后,它与-Xss129m:D – bholagabbar 2015-08-09 11:31:06
如果您想玩线程堆栈大小,您需要查看热点JVM上的-Xss选项。由于到JVM的-X参数是特定于分布的,IIRC,所以在非热点虚拟机上可能会有所不同。
在Hotspot上,如果您想将大小设置为16 megs,则看起来像java -Xss16M
。
类型java -X -help
如果您想查看您可以传入的所有特定于分发的JVM参数,我不确定这是否与其他JVM相同,但它会打印所有热点特定参数。
对于它的价值 - 我会建议限制您在Java中使用递归方法。这不是在优化他们太大 - 一个在JVM不支持尾递归(见Does the JVM prevent tail call optimizations?)。尝试重构上面的因子代码以使用while循环而不是递归方法调用。
我假设你通过堆栈跟踪中的循环线计算了“深度1024”?
显然,在Throwable的堆栈跟踪数组的长度似乎被限制在1024 试试下面的程序:
public class Test {
public static void main(String[] args) {
try {
System.out.println(fact(1 << 15));
}
catch (*Error 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);
}
}
+1一起用于实际深度检查... – helios 2010-09-15 14:25:07
奇怪!你说你想生成一个递归1个< < 15深度 ??? !!!!的
我建议不要尝试。堆栈的大小将为2^15 * sizeof(stack-frame)
。我不知道堆栈的大小是多少,但2^15是32.768。差不多......好吧,如果它停止在1024(2^10),你必须使它2^5倍大,它比你的实际设置大32倍。
这是正确的,但它没有回答我的问题。 – pts 2010-09-17 13:58:10
控制进程内堆栈大小的唯一方法是启动新的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 (*Error 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 (*Error e) {
System.err.println("true recursion level was " + level);
System.err.println("reported recursion level was "
+ e.getStackTrace().length);
}
}
}
由于您热衷于避免所有理智的方法,因此很难给出明智的解决方案。重构一行代码是可行的解决方案。
注意:使用-Xss设置每个线程的堆栈大小,这是一个非常糟糕的主意。
另一种方法是通过字节码操作来更改代码,如下所示;
public static long fact(int n) {
return n < 2 ? n : n > 127 ? 0 : n * fact(n - 1);
}
给出n> 127的每个答案都是0.这样可以避免更改源代码。
感谢您指出设置高堆栈大小会浪费内存的线程不需要它。还要感谢您指出,问题中的“事实”函数可以重构为使用更少的堆栈空间。 – pts 2010-09-17 22:14:47
@pts,你的感谢是注意到的。我认为在一个更复杂的用例中这是一个明智的问题,但这些非常罕见。 – 2010-09-18 14:46:40
其他海报已经指出如何增加记忆和你可以记忆电话。我建议,对于许多应用程序,您可以使用斯特林公式来近似大n!几乎没有内存占用,速度非常快。
采取甘德在这篇文章中,其具有的功能和代码的一些分析:
http://threebrothers.org/brendan/blog/stirlings-approximation-formula-clojure/
这是正确的,但它没有回答我的问题。 – pts 2014-03-20 16:20:55
我做Anagram excersize,这就好比Count Change问题,但有50种000面额(硬币)。我是not sure that it can be done iteratively,我不在乎。我只知道-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: *Error => // 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)
你会发现堆栈可以以指数级增长的指数级增长,并且指派更多的堆栈分配给该线程。
添加此选项
--driver-java的选项-Xss512m
到您的火花提交命令将解决这个问题。
看起来比它更简单。 fact()被递归地调用32K次。这应该小于1MB的堆栈。 : -/ – 2010-09-13 12:51:08
@Aaron:+函数开销,这是..一个LOT – halfdan 2010-09-13 12:53:04
除了你的堆栈问题。请注意,你正在炸毁你的长整数。 1 Sean 2010-09-13 13:36:00