Java中的遞歸調用

  Java中不合理的使用遞歸調用,可能會致使棧內存溢出,這點是須要注意的。java

  

  java將爲每一個線程維護一個棧,棧裏將爲每一個方法保存一個棧幀,棧幀表明了一個方法的運行狀態。 也就是咱們常說的方法棧。最後一個爲當前運行的棧幀。數據庫

  那麼每一次方法調用會爲新調用方法的生成一個棧幀,保存當前方法的棧幀狀態,棧幀上下文切換,切換到最新的方法棧幀。數組

 

  在遞歸和循環之間選擇時,應該優先選擇的是循環而非遞歸,特別是要避免深度的遞歸。函數

  

  關於遞歸還須要瞭解的是尾遞歸調用,尾遞歸調用是能夠被進行優化的。優化

  尾調用指的是一個方法或者函數的調用在另外一個方法或者函數的最後一條指令中進行。下面定義了一個foo()函數做爲例子:spa

int foo(int a) {
  a = a + 1;
  return func(a);
}

  尾調用,不僅是尾遞歸,函數調用自己均可以被優化掉,變得跟goto操做同樣。這就意味着,在函數調用前先把棧給設置好,調用完成後再恢復棧的這個操做(分別是prolog和epilog)能夠被優化掉。線程

 

  函數式語言的開發人員常常使用遞歸,因此大多數函數式語言的解釋器都會進行尾調用的優化。可是在Java中使用深度的遞歸必定要很是的當心,不然頗有可能會致使棧溢出的發生。code

  

  下面是不合理使用遞歸的例子:對象

package test;

public class RecursiveTest {
    /**
     * 遞歸實現
     * 
     * @param n
     * @return
     */
    public static double recursive(long n) {
        if (n == 1) {
            return Math.log(1);
        } else {
            return Math.log(n) + recursive(n - 1);
        }
    }

    /**
     * 非遞歸實現
     * 
     * @param n
     * @return
     */
    public static double directly(long n) {
        double result = 0;
        for (int i = 1; i <= n; i++) {
            result += Math.log(i);
        }
        return result;
    }

    public static void main(String[] args) {
        int i = 5000000;
        long test = System.nanoTime();
        long start1 = System.nanoTime();
        double r1 = recursive(i);
        long end1 = System.nanoTime();
        long start2 = System.nanoTime();
        double r2 = directly(i);
        long end2 = System.nanoTime();

        System.out.println("recursive result:" + r1);
        System.out.println("recursive time used:" + (end1 - start1));
        System.out.println("non-recursive result:" + r2);
        System.out.println("non-recursive time used:" + (end2 - start2));
    }
}

  

  JVM中可能致使內存溢出的其餘緣由還包括: blog

  • 引用變量過多使用了Static修飾 如public staitc Student s;在類中的屬性中使用 static修飾的最好只用基本類型或字符串。如public static int i = 0; //public static String str;
  • 使用了大量的遞歸或無限遞歸(遞歸中用到了大量的建新的對象)
  • 使用了大量循環或死循環(循環中用到了大量的新建的對象)
  • 是否使用了向數據庫查詢全部記錄的方法。即一次性所有查詢的方法,若是數據量超過10萬多條了,就可能會形成內存溢出。因此在查詢時應採用「分頁查詢」。
  • 是否有數組,List,Map中存放的是對象的引用而不是對象,由於這些引用會讓對應的對象不能被釋放。會大量存儲在內存中。
  • 是否使用了「非字面量字符串進行+」的操做。由於String類的內容是不可變的,每次運行"+"就會產生新的對象,若是過多會形成新String對象過多,從而致使JVM沒有及時回收而出現內存溢出。

  如String s1 = "My name";

  String s2 = "is";

  String s3 = "xuwei";

  String str = s1 + s2 + s3 +.........;這是會容易形成內存溢出的

相關文章
相關標籤/搜索