内存泄露与垃圾回收机制

2023-05-29 09:29:40 浏览数 (1)

一、内存泄露

不再使用的内存,没有及时被释放,机会造成内存泄露

二、垃圾回收机制

因为收回过程内存开销比较大,且堵塞其他操作,所以V8引擎会周期性的释放那些不再使用的变量,进而释放内存

1. 全局变量:生命周期较长,直到卸载当前页面才会回收

2. 局部变量:如函数内的变量(排除个别闭包),一旦函数执行完毕,该变量即会被回收

2. 闭包:分为两种,一种是程序自动生成的闭包,会遵循局部变量规则,但手动生成的闭包,V8引擎不会自动回收,需要开发者人为的将变量置为null,才会被回收。后期单独讲解闭包

三、方式

主要分为两种,标记清除和引用计数

1. 标记清除:

变量进入执行上下文时,会打个进入环境的标记,当离开执行环境时,再打个离开环境的标记,在下一个回收周期内,会将离开执行环境的变量进行回收

2. 引用计数:

当声明了一个变量并将一个引用类型值赋给该变量时,则这个值的引用次数就是 1。如果同一个值又被赋给另一个变量,则该值的引用次数加 1。

相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的引用次数减 1。当这个值的引用次数变成 0 时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间回收回来。如:

代码语言:javascript复制
function fn() {
    var q = {}; // q 指向对象的引用次数为 1
    var a = q;  // q 指向对象的引用次数加 1,为 2
    var c = q;  // q 指向对象的引用次数再加 1,为 3
    var a = {}; // q 指向对象的引用次数减 1,为 2
}

这样,当垃圾回收器下次再运行时,它就会释放那些引用次数为 0 的值所占用的内存,进而无法刚问引用次数为0的变量。

但该方式会存在一个bug,即循环引用的变量不会被回收,如:

代码语言:javascript复制
function fn() {
    var q = {};  // q 指向对象的引用次数为 1
    var a = {};  // a 指向对象的引用次数为 1
    q.obj = a;  // a 指向对象的引用次数为 2
    a.obj = q;  // q 指向对象的引用次数为 2
}
fn();

即使fn已经执行完毕,理论上q和a应该会被回收,但由于引用次数不为0,就造成了不会被回收

四、哪些场景会造成内存泄露

  • 不合理使用闭包,内部有外部变量引用的话,那么这个变量就一直在内存中无法回收
  • 不合理使用定时器,已结束使用定时器,但未取消,且定时器内部有外部变量引用的话,那么这个变量就一直在内存中无法回收
  • 不合理获取DOM元素,DOM已经被删除,且程序一直保存该DOM的引用,导致无法被回收

0 人点赞