垃圾回收机制【JS深入知识汇点15】

145 阅读3分钟

js 有自动垃圾回收机制,会定期对那些我们不再使用的变量、对象所占用的内存进行释放。JS 的回收机制很简单:找出不再使用的变量,然后释放掉其占用的内存,但这个过程不是时时的,因为其开销比较大,所以垃圾回收器会按照固定的时间间隔周期性的执行。

内存管理

内存管理包括:

  • 内存分配:申明变量、函数、对象,系统会自动分配内存
  • 内存使用:读写内存,使用变量、函数等
  • 内存回收:使用完毕后,由垃圾回收机制自动回收不再使用的内存

内存分配

JS 内存空间的类型:

  • 堆:存放复杂对象
  • 栈:存放常见的基础类型和函数,存在于栈中的数据大小和生存期是确定的,可以明确知道每个区块的大小,因此栈的速度快于堆(后进先出)
  • 池:一般归为栈中,存放常量

内存回收

JS 中存在两种变量 ——— 全局变量和局部变量,全局变量会从执行函数开始,到函数执行结束。局部变量在函数中,从执行函数开始,到函数执行结束,这些局部变量所占用的空间会被释放。

还有一种情况就是,局部变量不会随着函数的结束而被回收,那就是局部变量被函数外部的变量使用(比如必包)

标记清除(常用)

当变量进入执行环境时,标记为“进入环境”,当变量离开执行环境时,则标记“离开环境”。标记为“离开环境”的变量则可以被回收。

引用计数

统计引用类型变量声明后被引用的次数,当次数为0时,该变量被回收。

但引用计数有一个明显的缺点是 —— 循环引用

JS V8 引擎的垃圾回收机制(分代回收)

将保存对象的进行了分代:

  • 对象最初被分在 新生代,当指针达到新生区的末尾,会有一次垃圾回收清理(小周期),清理掉新生代中不再活跃的死对象
  • 对于超过 2 个小周期的对象,则需要将其移动到 老生区 。

内存泄漏

虽然 JS 会自动回收垃圾,但是如果我们的代码写法不当,会让代码一直处于 “进入环境” 的状态,无法被回收

意外的全局变量

function test() {
	hh = 1 //  创建了全局变量hh
    this.variable = 2  // this 指向全局对象window
}

被遗忘的计时器或回调函数

var someRes = getData();
// 如果id为Node的元素从 DOM 中移除,该定时器仍会存在,最外层的 someRes 也不会被释放
setInterval(function() {
	var node = document.getElementById('Node')
    if (node) {
    	node.innerHTML = JSON.stringify(someRes)
    }
}, 1000)

闭包

闭包可以维持函数内局部变量,使其得不到释放。

// 解决办法1:将事件处理函数定义在外面
function bindEvent() {
	var obj = document.createElement('xxx')
    obj.onclick = onclickHandler
}
// 解决办法2:在定义事件处理函数的外部函数中,删除对 dom 的引用
function bindEvent() {
	var obj = document.createElement('xxx')
    obj.onclick = function() {...}
    obj = null
}

内存泄漏的识别方法

步骤:

  1. 打开控制台,切换到 performance
  2. 勾选 Screenshots 和 memory
  3. 左上角小圆点开始录制
  4. 停止录制

如果垃圾回收之后的最低值min,在不断上涨,那么肯定有较严重的内存泄漏问题。

避免内存泄漏的方法

  1. 减少不必要的全局变量
  2. 避免死循环
  3. 避免创建过多的对象
  4. 对象尽量复用
  5. 在循环中的函数表达式,能复用最好放在循环外面

好题分享:

Q1: 什么是垃圾?

没有被引用的变量就是垃圾

Q2:如果回收垃圾

使用标记清除法