簡體   English   中英

具有Integer對象的Java遞歸方法StackOverflowError

[英]Java recursion method StackOverflowError with Integer object

我一直在嘗試制作一個斐波那契數字返回器(假設輸入n返回斐波那契序列中索引位置n的元素)。 我試圖使其既遞歸又具有較低的空間復雜度(我不實例化任何新變量)。 我將Integer對象用作值,盡管我知道它們的值會溢出(它們返回負值),但這實際上是故意的(出於教育目的)。 該功能之所以稱為smartestFib,是因為它的空間復雜度低於我的其他功能。

當我將smartestFib(n)的值設置為130或更高時,就會出現問題。 對於129或更低的版本,它工作得很好(考慮到溢出),但是對於130或更高版本,它給出了例外。 主要問題是我無法找出異常的真正含義:它顯示了太多錯誤,導致我看不到第一個錯誤,因此不知道確切的錯誤是什么。 因為我不知道錯誤的類型,所以我無法捕獲它。

我的代碼是:

private static int smartestFib(Integer goalNum)
{   
    if (goalNum < 2)
        return 1;
    else
        return smartestFib(goalNum-2, 0, 1,1);
}

private static int smartestFib(Integer goalNum, Integer currentNum, Integer lastValue, Integer beforeLastValue) 
{   
    if (goalNum == currentNum)
        return lastValue + beforeLastValue;
    else
    {
        return smartestFib(goalNum, currentNum + 1, lastValue+beforeLastValue,lastValue);
    }
}

對於這個問題,我將不勝感激,因為問題的任意性使我相信這是我不知道的技術問題,也不知道在哪里尋找。 提前非常感謝。

編輯:顯然這可能是一個StackOverflowError,非常感謝! 但是現在我想知道,我是否還有其他功能,具有更高的空間復雜度,並且它們沒有這個問題。 怎么樣?

private static int smarterFib(int goalNum)
{
    assert (goalNum >= 0): "The variable goalNum may not negative.";

    if (goalNum < 2)
        return 1;
    else
    {
        ArrayList<Integer> sequenceList = new ArrayList<Integer>(Arrays.asList(new Integer[] {1,1}));
        return smarterFib(goalNum-2, sequenceList);
    }
}

    private static int smarterFib(int goalNum, ArrayList<Integer> priorValues) 
{
    assert (goalNum >= 0): "The variable goalNum may not negative.";
    assert (priorValues != null): "The array priorValues has not been initialized.";

    if (goalNum <= priorValues.size()-2)
        return (priorValues.get(priorValues.size()-1) + priorValues.get(priorValues.size()-2));
    else
    {
        priorValues.add(priorValues.get(priorValues.size()-1) + priorValues.get(priorValues.size()-2));
        return smarterFib(goalNum, priorValues);
    }
}

我不明白為什么這個不會引起問題,而我的新的卻會引起問題。

遞歸程序通過再次調用相同的方法並獲得收益(即通過調用該方法的線程創建越來越多的stackframes )來工作,並在達到默認堆棧大小之后,將得到stackoverflowerror

因此,要解決此問題,您需要通過傳遞-Xss作為JVM參數來增加堆棧大小,您可以在此處查看

我還有其他功能,具有更高的空間復雜度,而且它們沒有此問題。 怎么樣?

因此,第一和第二程序之間的區別如下:

區別在於,一個使用Boxing(當對goalNum變量使用Integer類型時),另一個使用原始int ;當您使用Boxing時,這會導致性能問題,並且程序無法完成。

因此,將您的goalNum變量從Integer類型更改為int ,然后它可以工作(我測試過):

private static int smartestFib(int goalNum, int currentNum, 
     int lastValue, int beforeLastValue) {   
         System.out.println(goalNum);
         if (goalNum == currentNum)
             return lastValue + beforeLastValue;
         else {
            return smartestFib(goalNum, 
                   currentNum + 1, lastValue+beforeLastValue,lastValue);
        }
 }

因此,總而言之,請始終避免不必要的裝箱/拆箱(從原語轉換為包裝器類型,即從intInteger ),這在您運行大量計算時(例如在遞歸中)變得更加重要 您可以在此處查找有關拳擊如何導致執行問題的更多信息。

堆棧溢出不是基於例程的空間復雜度。 這意味着您已經使用了調用堆棧上的空間。 每個活動函數調用都占用一些堆棧空間,跟蹤傳入和傳回的值,並經常進行上下文切換空間(寄存器值等)。

SmarterFib傳遞一個整數(一個字)和一個數組(一個字作為參考;總內存使用量為N (目標)字,外加幾個字的開銷)。 堆棧上總共有2N個字。

SmartestFib為每個調用傳遞四個整數,即堆棧上的4N個字。

我通常不希望它們對運行時的堆棧有完全不同的要求:如果SmartestFib在N = 130時將堆棧炸毀,我希望SmarterFib在N = 260之前就將其炸毀(有固定的通話開銷)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM