关键词

垃圾回收、内存泄漏

垃圾回收

为什么要垃圾回收?

js的变量没有固定大小,是动态分配的,所以需要进行垃圾回收,避免内存占用满了之后造成系统崩溃。

垃圾回收的机制/特点

对于栈内存 切换到别的上下文就回收
对于堆内存 分为新生代和老生代

含义:新生代指的是只经过一次垃圾回收的对象,老生代指的是经过多次垃圾回收的对象

具体:新生代被分为from和to两个空间。to一般闲置,在from的空间满了之后执行scavenge算法进行垃圾回收。

执行垃圾回收的时候应用逻辑将停止,等垃圾回收结束后继续执行

什么时候回收?

利用浏览器的空闲时间

scavenge算法

算法分为三步:

  1. 检测from空间的存活对象,查看是否满足老生代条件(to是否超过25%/是否经历过一次垃圾回收)?老生代:移动到to空间
  2. 如果对象不存活则释放对象的空间
  3. 最后将from和to空间进行角色交换

新生代到老生代

条件有两个

  1. 我们知道新生代的条件是只经过一次垃圾回收,也就意味着它经历了一次Scavenge
  2. to空间的内容使用占比。因为scavenge算法结束之后会交换fromto的位置,所以to的空间不能太小,影响到后续的内存分配。所以当从from到to的时候,如果to的空间超过25%,直接变成老生代。

老生代的回收机制

老生代采用了标记清除和标记压缩法

标记清除:在回收的过程中标记存活的对象,结束后清除没有标记的对象(释放了空间)

不利:造成了很多内存碎片,不利于后续内存分配,引入标记压缩法

标记压缩法:

其他的方法

增量标记法:垃圾回收的过程会暂停应用逻辑,对于新生代来说还好,对于老生代来说时间长,所以引入增量标记分割停顿的过程,执行一段分割就运行一会逻辑,交替。

内存泄漏

  • 意外的全局变量:无法被回收
  • 定时器:未被正确关闭,导致所引用的外部变量无法被释放。
  • 事件监听:没有正确销毁(低版本浏览器可能出现)
  • 闭包
    • 第一种情况是我们使用未声明的变量,而意外的创建了一个全局变量,而使得这个变量一直留在内存中无法被回收
    • 第二种情况是我们设置了setInterval定时器,而忘记取消他,如果循环函数对外部变量有引用的话,那么这个变量会被一直留在内存中,无法被回收。
    • 第三种情况是我们获取一个DOM元素的引用。而后面的元素被删除。由于我们一直保留了对这个元素的引用,所以他也无法被回收。
    • 第四种情况是不合理的使用闭包,从而导致某些变量一直被流在内存当中。
  • dom引用:dom元素被删除时,内存中的引用未被正确清空。
  • 控制台console.log打印的东西

可以用chrome中的timeline进行内存标记,可视化查看内存的变化情况,找出异常点。