Memory 与Java7相比,运行相同递归代码的同一线程在Java8中似乎消耗了更多的堆栈内存

Memory 与Java7相比,运行相同递归代码的同一线程在Java8中似乎消耗了更多的堆栈内存,memory,recursion,java-8,stack-overflow,jvm-hotspot,Memory,Recursion,Java 8,Stack Overflow,Jvm Hotspot,我在“stackoverflow”网站上询问一个关于“java堆栈溢出”的问题:) 在Oracle Java 7(64位)中,对特定输入进行递归函数调用的特定线程在配置的堆栈大小为228k(-Xss228k)时运行良好 但是,对于相同的输入,运行相同递归代码的同一线程在Oracle java 8(64位)中抛出一个java.lang.StackOverflowerError,堆栈大小为228k。如果堆栈大小增加到512k(-Xss512k),它在Java8中运行良好 你知道为什么会这样吗?与Ja

我在“stackoverflow”网站上询问一个关于“java堆栈溢出”的问题:)

在Oracle Java 7(64位)中,对特定输入进行递归函数调用的特定线程在配置的堆栈大小为228k(-Xss228k)时运行良好

但是,对于相同的输入,运行相同递归代码的同一线程在Oracle java 8(64位)中抛出一个java.lang.StackOverflowerError,堆栈大小为228k。如果堆栈大小增加到512k(-Xss512k),它在Java8中运行良好

你知道为什么会这样吗?与Java 7相比,Java 8(Hotspot JVM)是否有任何变化,这可能会增加递归函数调用的堆栈内存消耗?如果需要,我可以提供其他详细信息

(编辑)注意:相同的递归深度在Java 7中“始终”有效,但在Java 8中对于228k的堆栈大小“始终”无效

我为不同的递归场景编写(静态或实例方法,不同数量的int参数)。以下是使用
-Xss228k
选项在64位HotSpot JVM的不同版本上的结果(发生
StackOverflowerError
之前的调用数)。请注意,运行之间的数量有所不同(我使用每个JVM启动了两次):

很可能
实例/0
静态/1
相同,等等,因为实例调用需要将
这个
作为附加参数传递

因此,JDK 7和JDK 8之间允许的递归调用的数量确实有所下降(除了
Static/0
case之外):您丢失了大约30-40个调用(大约占总数的5%)。因此,在您的应用程序中,您可能非常接近极限。顺便说一下,我注意到
-Xss256k
-Xss260k
之间有一个突然的跳跃(在1.8.0(u 40上测试):

因此,您可以尝试将堆栈大小增加到
-Xss260k
,它应该足以完成您的任务

顺便说一下,32位JVM允许使用相同的
-Xss228k
进行更多调用:

          St/0  St/1  St/2  St/3  St/4  In/0  In/1  In/2  In/3  In/4
7u67_32b  7088  5078  4655  4297  3990  5078  4655  4297  3990  3724
7u67_32b  6837  5092  4667  4308  4001  5092  4667  4308  4001  3734

因此,您也可能已经从32位Java-7切换到64位Java-8。在这种情况下,当然需要更多的堆栈空间,因为即使使用压缩的OOPs,堆栈中的指针似乎也是64位的,因此占用了更多的空间。

堆栈消耗量并不完全可预测。如果没有代码示例,就无法判断几十个可能的原因中是否有一个适用于您的特定问题。例如,请参见@Holger,相同的递归深度在Java 7中“始终”有效,但在Java 8中对于228k的堆栈大小“始终”失败。那么,非确定性因素是否仍然适用于我的场景?同时,我将尝试尽快给出一个代码示例。您的JVM都是64位的吗?都是32位?一个是32位,另一个是64位?@TagirValeev两个JVM都是64位的。所以您的测试没有给JIT一个启动的机会?如链接问题所示,它可以通过因子6改变结果…@Holger,出于某种原因,St/0是不太稳定的情况(见1.8.0_25/St_0),并在链接问题中进行了测试。对于JIT编译的代码,递归级别可以高得多(在第一次测试中最多可调用13000次)。问题是编译器是否有足够的时间进行JIT编译。通常程序在第一个StackOverflow错误时失效,因此我省略了对进一步启动的分析。总的来说,我的结果和你的一致。可能在St/0中,JIT编译器有一些机会。当然,结果可能与更复杂的方法体(后缘等)不同@TagirValeev非常感谢您提供的详细答案!在我的例子中,两个JVM都是64位的。正如您所提到的,在我的场景中似乎是这样的:“因此,JDK 7和JDK 8之间允许的递归调用数量(静态/0情况除外)确实有所下降:您丢失了大约30-40个调用(大约5%)总计数。因此,在您的应用程序中,您可能非常接近极限。顺便说一句,我注意到-Xss256k和-Xss260k之间突然跳变。因此,您可以尝试将堆栈大小增加到-Xss260k,这应该足以完成您的任务。”@Tagirvalev我还打算尝试提出一个228K到512k之间的堆栈大小,这足以满足我的线程。我很可能会按照您的建议将堆栈大小设置为260k:)顺便问一下,您是否知道为什么我们观察到Java 7和Java 8之间允许的递归调用数量下降了5%(大约)?有没有Java8文档讨论过可能导致这种降级的更改?@SanjayBhat,没有,我不知道。我想没有具体说明。
          St/0  St/1  St/2  St/3  St/4  In/0  In/1  In/2  In/3  In/4
-Xss256k  2724  2476  2270  2095  1945  2476  2270  2095  1945  1816
-Xss260k  4493  3228  2959  2731  2536  3228  2959  2731  2536  2367
          St/0  St/1  St/2  St/3  St/4  In/0  In/1  In/2  In/3  In/4
7u67_32b  7088  5078  4655  4297  3990  5078  4655  4297  3990  3724
7u67_32b  6837  5092  4667  4308  4001  5092  4667  4308  4001  3734