【问题标题】:Best way to get available remaining stack memory in Java?在Java中获得可用剩余堆栈内存的最佳方法?
【发布时间】:2012-01-20 05:36:16
【问题描述】:

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

(我正在尝试对深度递归调用进行分段,以尽可能多地使用堆栈(为了提高速度性能),但不会遇到堆栈溢出。

我已经做了一个“堆”版本,它带来了速度性能开销,这就是我做这个优化的原因。)

【问题讨论】:

    标签: java stack


    【解决方案1】:

    你需要它做什么?好奇而已?度量单位是什么 - 字节或递归调用的数量?

    您总是可以进行无限递归调用,捕获StackOverflowError 并计算堆栈帧数

    【讨论】:

    • 不,我正在尝试对深度递归调用进行分段,以尽可能多地使用堆栈,但不会遇到堆栈溢出。字节会很好,尽管剩余递归调用的数量会更好!如果不是立即可用,我不介意计算它。
    【解决方案2】:

    没有办法以便携的方式做到这一点。

    这不仅是特定于操作系统的,实际上堆栈的最大大小受多种约束(ulimit -c、可用虚拟内存量、-Xss-XX:ThreadStackSize 设置等)。这使得很难知道哪个约束将首先受到影响,即使您可以可靠地测量到目前为止已经消耗了多少堆栈空间。

    【讨论】:

    • 好吧,为了安全起见,我不介意取这些值中的最低值。 Xss 值永远不能 100% 依赖吗?您是否使用过 java.lang.management 包,并且可以证明其中没有任何内容允许计算当前堆栈使用情况和/或剩余堆栈空间?
    【解决方案3】:

    嗯。如果您担心,可以始终将深度计数器作为递归的一部分。

    【讨论】:

    • 不幸的是,递归调用的输入并不总是相同的,因此计数器不允许我估计在遇到堆栈溢出之前我还能执行多少次调用。
    • @Navigateur 您能否详细说明“递归调用的输入并不总是相同的”?既然局部变量的数量是提前知道的,你怎么会不知道呢?
    • 它接受 Object 作为输入,并通过反射通过它,因此它可以是任何类型,因此在每种情况下都可以是任何大小或深度。
    【解决方案4】:

    我会编写减少递归的方法。通常有一些方法可以减少(或不)递归调用。

    如果您递归地对列表求和,将第一个值添加到其余值的总和中,这将调用深度为 N。但是,如果您将列表切成两半并对值求和。 (如果列表中只有一个则返回值)递归深度为log2(N)。

    【讨论】:

    • 我已经这样做了。但是现在,为了性能,我想优化它以尽可能多地使用调用堆栈而不会发生堆栈溢出,这就是为什么我需要剩余堆栈空间的最佳可用近似值。
    • 需要明确的是,非递归版本会带来速度性能开销,这就是我进行此优化的原因。
    • 为什么使用整个调用堆栈会提高性能?通常将算法更改为非递归可以提高性能。通话费用可能相对较高。
    • 它会增加太多的代码复杂性来减少实际的调用次数,因为它是一种间接递归,它根据输入采用不同的路径。因此,排除递归(以避免堆栈溢出)只会增加将事物从堆栈传输到堆的开销。所以纯堆栈版本的速度大约是 1.3 - 2 倍,这就是为什么这个优化是我唯一方便的选择。如果我可以轮询或计算剩余的堆栈空间量,我需要的代码更改是微不足道的。
    • 可能有一种方法可以避免经常调用它。当记录已知安全深度时,您可以有一个记录深度的属性和一个字段。如果您超过了已知的安全深度,则仅执行测试。
    【解决方案5】:

    我很惊讶迭代方法的性能较差。通常,由于方法调用的开销,递归方法会更慢。如果您的算法可以实现为尾递归,则几乎可以肯定它作为迭代实现会更快。你能告诉我们更多关于你真正想做的事情吗?也许性能上的差异不仅仅是为了递归而切换迭代。这是来自一些CS lecture notes 的示例,它引用了计算斐波那契数的递归方法,即 O(2^n),而迭代方法是 O(n)。我相信(尽管我没有尝试过)可以编写一个 O(n) 的递归斐波那契数生成器。

    编辑:

    最后一个想法。恕我直言,最好使用没有堆栈溢出问题的较慢方法,而不是引入尝试确定您即将溢出堆栈并具有一些回退机制来避免它的所有复杂性。

    【讨论】:

    • 我无法避免方法调用的开销,因为它是一种间接递归,它根据输入采用不同的路径。因此,排除递归只会增加将内容从堆栈传输到堆的开销。当它在功能上相同时,它会慢大约 2 倍。如果我有剩余的堆栈空间,我可以使用堆栈达到其极限,并收回大量的成本,而不会发生堆栈溢出。这在代码中是微不足道的,只需要在递归和堆放版本之间切换,我已经拥有了。
    猜你喜欢
    • 2012-12-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-12
    • 2013-02-07
    • 1970-01-01
    • 2012-03-14
    • 2020-12-14
    相关资源
    最近更新 更多