Skip to content

递归调用函数导致的栈溢出

栈溢出

  • 程序中的函数调用是通过栈实现的。每一次函数执行,都会把方法压到执行栈中,方法调用结束后就会弹出该栈帧。
  • 栈的大小是有限的,递归调用次数过多的话就会导致栈溢出,触发(js)Maximum call stack size exceeded异常,程序崩溃
js
// 溢出示例
(function getStackDepth(){
  try {
    // let obj = {} // 新声明的变量会占用栈空间
    return 1 + getStackDepth()
  } catch (e) {
    return 1 // Call stack overflow
  }
})()
11411 // chrome 结果
// 数量取决于方法体所占用的空间,占用空间越大,可用栈数量越小。可尝试在方法中增加变量,会发现结果变小。

优化

    1. 使用循环替代递归(大部分递归都可以用循环实现)
    1. 尾递归:指在方法返回时只调用本身,且不能包含表达式。编译器或解释器会把尾递归做优化,使递归方法不论调用多少次,都只占用一个栈帧,所以不会出现栈溢出。【Java没有尾递归优化】
js
// 用递归实现总和
const sum = (n) => {
  if (n <= 1) return n
  return n + sum(n-1) // 包含表达式,非尾递归
}
sum(10) // 10
sum(100000) // 爆栈 VM505:2 Uncaught RangeError: Maximum call stack size exceeded

// 尾递归实现
const sum = (n, prevSum = 0) => {
  if (n <= 1) return n + prevSum;
  return sum(n-1, n + prevSum) // return时调用本身
}

// 循环实现
function sum(num){
  let res = 0
  while(num--){
    res += num + 1
  }
  return res
}
sum(100000) // 正常输出 5000050000