JavaScript:堆栈溢出&&内存泄漏

在JavaScript中,会有听到两个概念:堆栈溢出和内存泄漏,这两种机制在开发中遇到的不多,但是一旦碰到就很头疼。下面就分别来讲述一下二者的概念,触发原因以及解决办法。

  • 堆栈溢出:

什么是堆栈溢出?我们知道JS中的数据存储分为栈和堆,程序代码运行都需要一定的计算存储空间,就是栈了,栈遵循先进后出的原则,所以程序从栈底开始运行计算,程序内部函数的调用以及返回会不停的执行进栈和出栈的操作,栈内被所占的资源也在不断的对应变化,但是一旦你的调用即进栈操作过多,返回即出栈不够,这时候就会导致栈满了,再进栈的就会溢出来。打个比方:就好像是跟女朋友去吃火锅,点了一个鸳鸯锅,俩人开始一人吃辣的一人吃不辣的,很和谐,结果你贪吃,放了很多菜在辣的一边,你又吃不过来,辣油溢出来到不辣的一边,那么你的女朋友就不高兴了,最终你肯定会被骂了。当然了,JS堆栈溢出后不会骂你,但是他会报错然后罢工了。再来看一个网上使用比较多模拟的代码(递归)的例子:

function isEven(n) {
    if (n === 0) {
        return true;
    }
    if (n === 1) {
        return false;
    }
    return isEven(Math.abs(n) - 2);
}

当我们打印console.log(factorial(10))答案是true,结果运行也比较快,再看当我们输入console.log(factorial(10000000)),结果是抛出了错误:Uncaught RangeError: Maximum call stack size exceeded(此运行在谷歌浏览器测试),这个错误的意思就是:最大调用超过堆栈大小,这是为什么呢?原因就是,程序在执行代码过程中,需要一定的计算空间即栈,一般大小为1M左右,当你每次调用程序内的函数等其它时,这些就会占用一定的空检,当占用过多时,就会超过该程序所分配的栈的空间,就会报错了。那么,如何解决这个问题?就拿上面的递归例子来说,解决办法如下(前文我们提到了闭包,这里就用闭包和Trampoline(蹦床原理)来解决):

function isEven(n) {
    function isEvenInner(n) {
        if (n === 0) {
            return true;
        }
        if (n === 1) {
            return false;
        }
        return function () {
            return isEvenInner(Math.abs(n) - 2);
        }
    }
    function trampoline(func, arg) {
        var value = func(arg);
        while (typeof value === "function") {
            value = value();
        }
        return value;
    }
    return trampoline.bind(null, isEvenInner)(n);
}
  • 内存泄漏

什么是内存泄漏?内存泄漏是指程序被分配的栈内有一块内存既不能使用,也不能被回收。就是你和你女朋友吃火锅,中间有一块位置没有汤,不能烫菜一样的。导致内存泄漏的原因一般有一下几种情况:

  • 函数内未使用声明变量关键字的变量
function func() {
    a = 1;
}
  • 未销毁的定时器
setInterval(function () {
    console.log(1)
}, 1000);
  • DOM以外的节点引用
var elements = {
    button: document.getElementById('button'),
};
function doStuff() {
    button.click();
}
function removeButton() {
    document.body.removeChild(document.getElementById('button')); // 这时,我们仍然有一个引用指向全局中的elements。button这个节点仍在内存中,不会被回收。
}
  • 闭包的循环引用
function my(name) {
    function sayName() {
        console.log(name)
    }
    return sayName
}
var sayHi= my("tom")
sayHi() //tom

在函数my()内部创建的sayName()函数是不会被回收机制回收,如果闭包不被调用,由于闭包会携带包含它的函数的作用域,因此会比其他函数占用更多的内存。过度使用闭包可能会导致内存占用过多。

最后再多加一个概念,我们前文一直提到程序运行时所占的内存空间,并且在程序运行的时候不停的在进行进栈出栈,调用和销毁,这里就涉及到浏览器的垃圾回收机制。什么是垃圾?垃圾就是不再使用的变量,顾名思义,你这个变量没用了,抱歉,你就会被浏览器销毁,以此来腾出空间,浏览器常用的垃圾回收办法有两种:标记清除和引用计数。

标记清除

这个是最常用的方式。当变量进入执行时,会给上一个标记,就证明这个变量进入了代码的执行环境,当变量执行完毕,离开执行环境时,会被标记上离开了执行环境。此时的垃圾回收器会去掉环境中的变量以及被环境中的变量引用的标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。最后。垃圾收集器完成内存清除工作,销毁那些带标记的值,并回收他们所占用的内存空间。

引用计数

这种方式用的不多。引用计数,顾名思义就是计算这个变量被引用的次数。当一个变量被声明且赋上引用类型,这个变量的引用次数就会相应的加1,如果包含这个值的引用变量又附上了另外一个值,那么这个值的引用次数就相应的减1,当引用次数变为0的时候,就无法访问这个值了。当垃圾收集器运行的时候,就会回收引用次数为0的值所占的内存,并释放这些内存。

 

  • 17
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 这个错误的意思是 JavaScript内存不足。这意味着你的程序尝试使用更多内存,但是电脑上可用的内存不足以满足需求。 这种情况通常发生在你的程序中存在内存泄露(memory leak)或者你的程序使用了过多的内存。 解决方法可能包括: - 寻找并修复内存泄露 - 优化你的程序,减少内存使用 - 尝试使用更大的内存限制来运行你的程序(例如,使用 `node --max-old-space-size=4096 script.js` 运行你的程序) ### 回答2: "FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory" 是Node.js中的一个错误消息,表示JavaScript内存溢出,这通常是因为程序的内存需求超过了分配给Node.js进程的可用内存限制。 造成JavaScript内存溢出的原因可能有多种,其中包括: 1. 内存泄漏:程序中存在未释放的内存,导致占用的内存超过了可用内存限制; 2. 大对象:程序中创建了过多的大型对象,超出了可用的堆内存; 3. 递归嵌套:程序中存在无限递归或嵌套调用,导致堆栈溢出,进而导致堆内存溢出; 4. 大数据集:处理了过大的数据集,超过了可用的堆内存; 5. 不合理的配置:配置了不合理的Node.js的内存参数,导致分配给Node.js的堆内存不足。 解决"FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript heap out of memory"错误的方法有以下几种: 1. 调整Node.js的内存参数:可以通过命令行参数--max-old-space-size或--max-memory-restart来增加Node.js进程的可用堆内存空间,比如将其设置为8GB或16GB; 2. 优化代码:检查代码是否存在内存泄漏或过多的内存占用,确保及时释放不再使用的变量和对象; 3. 分块处理大数据集:对于大型数据集,可以将其切分为多个小块进行处理,避免一次性加载整个数据集到内存中; 4. 优化算法:对于需要大量计算的算法,可以考虑优化算法实现,减少内存开销; 5. 使用流式处理:对于处理大文件或网络请求等场景,可以使用流式处理,逐步读取和处理数据,减少一次性加载大量数据到内存中的压力。 通过以上方法,可以有效解决JavaScript内存溢出的问题,提高应用程序的稳定性和性能。 ### 回答3: "FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript内存不足",这是JavaScript运行时环境中的一个错误消息。它表示当JavaScript代码执行时,分配内存失败,导致堆内存不足。 这个错误通常出现在以下情况下: 1. 内存泄漏: JavaScript代码中可能存在内存泄漏问题,导致未释放的内存占用堆空间。 2. 大规模数据处理: 当JavaScript处理大量数据时,例如大型数组或对象,可能会超出可用的堆内存大小。 3. 递归深度过大: 如果JavaScript代码中存在递归函数,并且递归深度过大,堆内存可能会被耗尽。 4. 非优化的V8引擎: V8引擎是用于执行JavaScript的核心组件,如果使用的是非优化版本的V8引擎,可能会导致堆内存限制较低。 要解决这个问题,可以尝试以下几种方法: 1. 优化代码: 仔细检查代码中是否有内存泄漏问题,并确保及时释放不再使用的对象。 2. 分批处理数据: 当处理大量数据时,可以将其分为较小的批次进行处理,以减少对堆内存的需求。 3. 增加堆内存限制: 可以通过命令行参数调整Node.js的堆内存限制,例如--max_old_space_size参数。需要根据具体情况调整合适的堆内存大小。 4. 使用优化的V8引擎: 确保使用的是经过优化的V8引擎,以提高代码的性能和堆内存的利用率。 总之,处理"FATAL ERROR: CALL_AND_RETRY_LAST Allocation failed - JavaScript内存不足"错误需要仔细检查代码,并针对具体情况采取相应的措施来优化代码或增加堆内存限制。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值