Blog
Blog copied to clipboard
作用域与闭包 - 堆栈溢出和内存泄漏的原理及防范
堆栈溢出和内存泄漏的原理及防范
堆栈溢出
产生原因
- 上溢:栈满时再做进栈必定产生空间溢出
- 下溢:栈空时再做退栈也产生空间溢出
最常见的就是无限递归或递归层级过深,导致调用栈空间不足,从而导致栈上溢。
// 阶乘,若用递归实现,层级不能过深
const factorial = n => n <=1 ? 1 : n * factorial(n - 1)
// 斐波那契数列也一样
const fibonacci = n => n <= 1 ? 1 : fibonacci(n - 1) + fibonacci(n - 2)
解决方案
递归改循环
优化原理:所有运算均在一个执行上下文中执行,不用生成额外的上下文。
const factorial = n => {
let result = 1
while (n > 1) {
result *= n--
}
return result
}
const fibonacci = n => {
let tmp = 1
let result = 1
while (n > 1) {
[tmp, result] = [result, tmp + result]
n--
}
return result
}
尾调用优化
优化原理:函数返回回溯时不需要做任何额外的计算,故可以不用保存函数的入口环境。
尾调用的优化依赖于语言实现,其本质还是将尾调用优化为循环的实现方式。ES6之前没有对尾调用进行优化,还是会导致调用栈增长。
const factorial = (n, result = 1) => n <= 1 ? result : factorial(n - 1, n * result)
const fibonacci = (n, prev = 1, cur = 1) => n <= 1 ? cur : fibonacci(n - 1, cur, prev + cur)`
内存泄漏
内存泄漏主要
产生原因
由于疏忽或错误造成程序未能释放已经不再使用的内存。
例如不再需要的闭包、定时器及全局变量等未能及时解除引用。
解决方案
- 及时解除不再需要的引用,如闭包、定时器及全局变量等;
- 使用
WeakSet
、WeakMap
,它们对于值的引用是弱引用,只要外部的引用消失,内部的引用就会自动被垃圾回收清除。