在Java中获取可用剩余堆栈内存的最佳方法是什么?

5

在Java中,递归调用过程中获取或计算剩余可用的堆栈内存,最好的方法是什么?

(我正在尝试对深度递归调用进行分段,并尽可能使用堆栈以提高速度性能,但避免出现堆栈溢出。我已经完成了一个“堆”版本,但带来了速度性能开销,这就是为什么我正在进行此优化的原因。)

5个回答

1

无法以便携的方式完成此操作。

不仅如此,实际上栈的最大大小受到多个限制的约束(例如:ulimit -c、可用虚拟内存的数量、-Xss-XX:ThreadStackSize设置等),这使得很难知道哪个限制会首先达到,即使您可以可靠地测量到目前已使用了多少堆栈空间。


1
好的,我不介意将这些值中最低的值作为安全边界。Xss值永远不能百分之百地依赖吗?您是否使用过java.lang.management包,并且可以证明其中没有任何内容允许计算当前堆栈使用情况和/或剩余堆栈空间? - Navigateur

1
你需要它做什么?只是出于好奇吗?单位是字节还是递归调用次数?
你总是可以进行无限递归调用,捕获 StackOverflowError 并计算堆栈帧。

不,我正在尝试分段进行深度递归调用,以尽可能多地利用堆栈,但又不会触发堆栈溢出。字节数也可以,但剩余递归调用次数更好!如果它不是立即可用的话,我也不介意计算它。 - Navigateur

1
嗯,如果你担心的话,你可以在递归中保持一个深度计数器。

不幸的是,递归调用的输入并不总是相同的,因此计数器无法让我估计在遇到堆栈溢出之前还可以进行多少次调用。 - Navigateur
@Navigateur,您能详细说明一下“递归调用的输入不总是相同的”吗?由于局部变量的数量是预先知道的,那么怎么可能不知道呢? - Pacerier
它接受Object作为输入,并通过反射对其进行处理,因此它可以是任何类型,在每种情况下都可以是任何大小或深度。 - Navigateur

1

我会编写一个不那么递归的方法。通常有办法可以减少(或者不需要)递归调用。

如果你递归地对列表求和,将第一个值加到其余部分的总和中,这将导致深度为N的调用。然而,如果你把列表分成两半并求和。(如果列表中只有一个值,则返回该值),递归深度就是log2(N)。


只是为了明确,非递归版本带来了速度性能开销,这就是我进行此优化的原因。 - Navigateur
使用整个调用堆栈如何提高性能?通常将算法更改为非递归会提高性能。调用可能相对昂贵。 - Peter Lawrey
你可以调用一个使用了大量堆栈的方法,如果它抛出 StackOverFlowError 错误,那么你就知道你已经接近边缘了。 - Peter Lawrey
这是一个绝妙的想法。然而,它增加了一些额外负担。我想知道是否有更便宜的方式,或者是否存在更便宜的方式。 - Navigateur
1
可能有一种方法可以避免经常调用它。您可以拥有一个属性来记录深度和一个字段来记录已知的安全深度。如果超过已知的安全深度,那么才执行测试。 - Peter Lawrey
显示剩余2条评论

1

我很惊讶迭代方法的性能比递归方法差。通常由于方法调用的开销,递归方法会更慢。如果您的算法可以实现为尾递归,则几乎肯定作为迭代实现会更快。您能否告诉我们更多关于您实际要做什么的信息?也许性能差异更多地是算法上的问题,而不仅仅是将迭代换成递归。这里有一个例子来自一些CS讲义,它引用了一种递归方法来计算斐波那契数列,其时间复杂度为O(2^n),而迭代方法的时间复杂度为O(n)。我相信(虽然我没有尝试过),可能会编写一个递归斐波那契数生成器,其时间复杂度为O(n)。

编辑:

最后一点想法。在我看来,使用一个速度较慢但没有堆栈溢出问题的方法要好得多,而不是引入所有复杂性来确定您即将溢出堆栈,并具有一些回退机制以避免它。


我无法避免方法调用的开销,因为它是一种间接递归,根据输入采取不同的路径。因此,将递归分解只会增加从堆栈到堆的传输开销。当功能相同时,这会慢大约2倍。如果我还有剩余的堆栈空间,我可以使用堆栈直到其极限,并收回大量成本,而不会发生堆栈溢出。这在代码中很简单,只需要在递归和放入堆版本之间切换即可,我已经做到了。 - Navigateur

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接