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
如String s1 = "My name";
String s2 = "is";
String s3 = "xuwei";
String str = s1 + s2 + s3 +.........;這是會容易形成內存溢出的