Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/381.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 递归方法的间歇性堆栈溢出_Java_Recursion_Stack Overflow - Fatal编程技术网

Java 递归方法的间歇性堆栈溢出

Java 递归方法的间歇性堆栈溢出,java,recursion,stack-overflow,Java,Recursion,Stack Overflow,我为一个课堂作业写了一个简单的方法,它使用递归(是的,它必须使用递归)来计算分形图案中三角形的数量: public static BigInteger triangleFract(int layer) { if(layer < 0) { throw new IllegalArgumentException("Input must be >= 0"); } else if(layer == 0) { return new BigInteg

我为一个课堂作业写了一个简单的方法,它使用递归(是的,它必须使用递归)来计算分形图案中三角形的数量:

public static BigInteger triangleFract(int layer) {
    if(layer < 0) {
        throw new IllegalArgumentException("Input must be >= 0");
    } else if(layer == 0) {
        return new BigInteger("0");
    } else if (layer == 1) {
        return new BigInteger("1");
    } else {
        return triangleFract(layer - 1)
              .multiply(new BigInteger("3"))
              .add(new BigInteger("2"));
    }
}
公共静态BigInteger triangleFract(int层){
如果(层<0){
抛出新的IllegalArgumentException(“输入必须大于等于0”);
}else if(层==0){
返回新的BigInteger(“0”);
}else if(层==1){
返回新的BigInteger(“1”);
}否则{
返回三角形分形(层-1)
.multiply(新的大整数(“3”))
.add(新的BigInteger(“2”));
}
}
我一直想做的是理解int层有多大,从而限制用户输入。在一些测试之后,我得到大约6700+的堆栈溢出,这很好

让我不安的是,如果layer有数千个,该方法通常会运行,但它仍然会随机遇到
StackOverflowError

例如,我选择将图层限制为4444,它似乎几乎总是能够处理这个问题,但偶尔它似乎还是会溢出


为什么会这样?我能做些什么吗

考虑移动到迭代版本。这就是我所认为的,如果你开发一个递归算法,你必须控制层次深度,或者根本不使用递归。

允许递归到那个深度是一种设计风格

尝试以下迭代版本:

public static BigInteger triangleFract(int layer) {
    if (layer < 0) {
        throw new IllegalArgumentException("Input must be >= 0");
    }
    if (layer == 0) {
        return BigInteger.ZERO;
    }
    BigInteger result = BigInteger.ONE;
    BigInteger two = new BigInteger("2");
    BigInteger three = new BigInteger("3");
    for (int i = 1; i < layer; i++) {
        result = result.multiply(three).add(two);
    }
    return result;
}
公共静态BigInteger triangleFract(int层){
如果(层<0){
抛出新的IllegalArgumentException(“输入必须大于等于0”);
}
如果(层==0){
返回biginger.ZERO;
}
BigInteger结果=BigInteger.ONE;
BigInteger二=新的BigInteger(“2”);
BigInteger三=新的BigInteger(“3”);
对于(int i=1;i
注:

  • 使用
    biginger.ZERO
    biginger.ONE
    而不是为这些值创建新实例
  • 删除冗余的
    else
    -终止语句后没有
    else
    (例如
    return
  • 重新使用
    新的BigInteger(“2”)
    新的BigInteger(“3”)
    而不是每次迭代都创建新实例

也许JVM已经(通过转义分析)确定可以在堆栈而不是堆上分配BigInteger。根据何时实现此优化,所需的堆栈大小会有所不同


也就是说,可能还有许多其他原因,其行为可能取决于您使用的JVM。

实际上您可以做一些事情:增加最大堆栈大小。这是在JVM启动时使用选项
-Xss
完成的,如下所示:

java -Xss40m MainClass

小心不要设置过高的值。如果你必须超过60米-70米,那么建议重新设计你的代码。

我无法重现你的“波动”效应。这是非常确定的代码,因此每次都应该得到相同的结果(包括堆栈溢出错误)

你是怎么测试的?您是否为4444测试的每次尝试都运行了一个新的jvm?(或者仅仅是三角形(4444);在循环中调用?)

您的操作系统、java版本等是什么

我这样问是因为我真的不喜欢这样未解决的问题——像这样的事情会在哪里(什么时候)伤害你;)


哦。。。顺便说一句,无论如何,你应该使用BigInteger中的1和0常量(并为2和3创建你自己的常量。这应该为你节省不少内存(是的,我知道,这不是你的问题).

适用于那些无法再现这种波动的人。从方法可靠抛出的
堆栈溢出错误开始查找
值。该值越接近实际阈值越好。现在从循环内部调用此方法(在我的机器上
maxLayer=11500
):

它将抛出
StackOverflowerError
。现在将该值减小一点(看起来5-10%就足够了):

在我的机器上,这不会引发任何错误,并成功跳过
11500
。实际上,它在
16000
之前工作正常

因此,不管它是什么,它都可能涉及JVM优化。我试着用
-XX:+printcomployment
运行一个程序。我看到了JIT在循环时是如何工作的:

我发现它通常打印出小于100(95-98)的内容。这与我手动执行时看到的一致。当我跳过发射器时:

for I in `seq 1 100`; do 
        java \
        Triangle 2>&1| grep Stack; done | wc -l

它总是打印出100。

我想问:为什么是BigInteger?你也可以使用原语long。我在没有任何堆栈溢出错误的情况下多次运行了
triangleFract(7000)
@BlueBullet这将很快超过
long
s的容量(大约从
layer=41开始)@A.R.S.好的,我明白了。谢谢你的解释。@user1831889:是的,你可以尝试增加statck大小。如果你有那么多内存或-XSS512M,请将其设置为ega的固定值。他问了一个非常具体的问题:它为什么会波动?是的,他提到它必须使用递归。@Bohemian FYI
结果将是
2*pow(3,第1层)-1
。因此您可以直接返回
(新的BigInteger(“3”)).pow(第1层)。乘法(新的BigInteger(“2”)。减法(BigInteger.1)
,并完全避免循环。代码中有一个错误:
结果。乘法(3)。加(2);
将返回一个未分配给任何人的
BigInteger
。@SoboLAN你完全正确!
BigInteger
是不可变的!我犯了一个新手错误。我现在已经修复了代码。谢谢+1。还有一个使用迭代而不是递归的示例(遍历树):递归和迭代之间总是有一条线,有时递归是简化解
int i = 10500;
while (true) {
    System.out.println(i);
    triangleFract(i++);
}
117   1       java.lang.String::hashCode (64 bytes)
183   2       java.lang.String::charAt (33 bytes)
189   3       sun.nio.cs.UTF_8$Decoder::decodeArrayLoop (553 bytes)
201   4       java.math.BigInteger::mulAdd (81 bytes)
205   5       java.math.BigInteger::multiplyToLen (219 bytes)
211   6       java.math.BigInteger::addOne (77 bytes)
215   7       java.math.BigInteger::squareToLen (172 bytes)
219   8       java.math.BigInteger::primitiveLeftShift (79 bytes)
224   9       java.math.BigInteger::montReduce (99 bytes)
244  10       sun.security.provider.SHA::implCompress (491 bytes)
280  11       sun.nio.cs.UTF_8$Encoder::encodeArrayLoop (490 bytes)
282  12       java.lang.String::equals (88 bytes) 11400
289  13       java.lang.String::indexOf (151 bytes)
293  14       java.io.UnixFileSystem::normalize (75 bytes)
298  15       java.lang.Object::<init> (1 bytes)
298  16       java.util.jar.Manifest$FastInputStream::readLine (167 bytes)
299  17       java.lang.CharacterDataLatin1::getProperties (11 bytes)
300  18       NormalState::triangleFract (74 bytes)
308  19       java.math.BigInteger::add (178 bytes)
336  20       java.lang.String::lastIndexOf (151 bytes)
337  21       java.lang.Number::<init> (5 bytes)
338  22       java.lang.Character::digit (6 bytes)
340  23       java.lang.Character::digit (168 bytes)
342  24       java.lang.CharacterDataLatin1::digit (85 bytes)
343  25       java.math.BigInteger::trustedStripLeadingZeroInts (37 bytes)
357  26       java.lang.String::substring (83 bytes)
360  27       java.lang.String::lastIndexOf (10 bytes)
360  28       java.lang.String::lastIndexOf (29 bytes)
361  29       java.math.BigInteger::<init> (24 bytes)
361  30       java.lang.Integer::parseInt (269 bytes)
361  31       java.math.BigInteger::<init> (8 bytes)
362  32       java.math.BigInteger::<init> (347 bytes)
404  33       java.math.BigInteger::multiply (72 bytes)
404  34       java.math.BigInteger::add (123 bytes)
for I in `seq 1 100`; do 
        java ... com.intellij.rt.execution.application.AppMain \
        Triangle 2>&1| grep Stack; done | wc -l
for I in `seq 1 100`; do 
        java \
        Triangle 2>&1| grep Stack; done | wc -l