你所不知道的JavaScript⑤--垃圾回收与内存泄漏
关键词
垃圾回收、内存泄漏
垃圾回收
为什么要垃圾回收?
js的变量没有固定大小,是动态分配的,所以需要进行垃圾回收,避免内存占用满了之后造成系统崩溃。
垃圾回收的机制/特点
对于栈内存 切换到别的上下文就回收
对于堆内存 分为新生代和老生代
含义:新生代指的是只经过一次垃圾回收的对象,老生代指的是经过多次垃圾回收的对象
具体:新生代被分为from和to两个空间。to一般闲置,在from的空间满了之后执行scavenge算法进行垃圾回收。
执行垃圾回收的时候应用逻辑将停止,等垃圾回收结束后继续执行
什么时候回收?
利用浏览器的空闲时间
scavenge算法
算法分为三步:
- 检测from空间的存活对象,查看是否满足老生代条件(to是否超过25%/是否经历过一次垃圾回收)?老生代:移动到to空间
- 如果对象不存活则释放对象的空间
- 最后将from和to空间进行角色交换
新生代到老生代
条件有两个
- 我们知道新生代的条件是只经过一次垃圾回收,也就意味着它经历了一次Scavenge
- to空间的内容使用占比。因为scavenge算法结束之后会交换fromto的位置,所以to的空间不能太小,影响到后续的内存分配。所以当从from到to的时候,如果to的空间超过25%,直接变成老生代。
老生代的回收机制
老生代采用了标记清除和标记压缩法
标记清除:在回收的过程中标记存活的对象,结束后清除没有标记的对象(释放了空间)
不利:造成了很多内存碎片,不利于后续内存分配,引入标记压缩法
标记压缩法:
其他的方法
增量标记法:垃圾回收的过程会暂停应用逻辑,对于新生代来说还好,对于老生代来说时间长,所以引入增量标记分割停顿的过程,执行一段分割就运行一会逻辑,交替。
内存泄漏
- 意外的全局变量:无法被回收
- 定时器:未被正确关闭,导致所引用的外部变量无法被释放。
- 事件监听:没有正确销毁(低版本浏览器可能出现)
- 闭包
- 第一种情况是我们使用未声明的变量,而意外的创建了一个全局变量,而使得这个变量一直留在内存中无法被回收
- 第二种情况是我们设置了setInterval定时器,而忘记取消他,如果循环函数对外部变量有引用的话,那么这个变量会被一直留在内存中,无法被回收。
- 第三种情况是我们获取一个DOM元素的引用。而后面的元素被删除。由于我们一直保留了对这个元素的引用,所以他也无法被回收。
- 第四种情况是不合理的使用闭包,从而导致某些变量一直被流在内存当中。
- dom引用:dom元素被删除时,内存中的引用未被正确清空。
- 控制台
console.log
打印的东西
可以用chrome中的timeline进行内存标记,可视化查看内存的变化情况,找出异常点。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 鱼与前端🐟!
评论