垃圾回收和内存泄漏

# 垃圾回收和内存泄漏

  • 内存泄漏:没有及时释放不再使用的内存。
  • JS有自动垃圾回收机制,按照固定的时间间隔,周期性地找出不再使用的变量,然后释放其占用的内存。

[TOC]

# 一、垃圾回收机制

# 1.1 标记清除(常用)

  1. 当变量进入执行环境,被标记为“进入环境”。
  2. 当变量离开执行环境,被标记为“离开环境”。

# 1.2 引用计数(不常用)

  1. 如果一个值的引用次数为0,就表示该值不再用到,则释放被占用的内存。
  2. 如果一个值不再需要了,引用数却不为0,垃圾回收机制无法释放这块内存,从而导致内存泄漏。

# 1.2.1 解除引用

  • 一旦数据不再使用,最好将其值设为null来释放内存。
  • 作用:让值脱离执行环境,一变垃圾回收机制下次运行时将其回收。

# 二、内存泄漏

当代码写法不当,会让一直处于“进入环境”的状态,无法被回收。

[JavaScript中的垃圾回收和内存泄漏 (opens new window)](https://segmentfault.com/a/1190000019035469 (opens new window))

# 2.1 引起内存泄漏的可能

# 2.1.1 意外的全局变量

  • 没有被声明的变量,成了一个全局变量,在页面关闭之前不会被释放。
function foo(arg) {
    bar = "this is a hidden global variable";
}
1
2
3
  • 当this指向全局的时候,创建了全局变量。
function foo() {
    this.variable = "potential accidental global";
}
// foo 调用自己,this 指向了全局对象(window)
foo();
1
2
3
4
5
  • 对策:启用严格模式解析 JavaScript ,避免意外的全局变量。

# 2.1.2 没有被清除的定时器

# 2.1.3 闭包

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

# 2.1.4 没有被清理的DOM元素引用

# 2.2 查看内存是否泄漏

  • 新版本的chrome在 performance 中查看:
    1. 打开开发者工具 Performance
    2. 勾选 Screenshots 和 memory
    3. 左上角小圆点开始录制(record)
    4. 停止录制
  • Heap 对应的部分就可以看到内存在周期性的回落也可以看到垃圾回收的周期,如果垃圾回收之后的最低值(我们称为min),min在不断上涨,那么肯定是有较为严重的内存泄漏问题。

# 三、垃圾回收的使用场景优化

# 3.1 清空数组

arr.length = 0;
// arr = []; 虽然让a变量成一个空数组,但是在堆上重新申请了一个空数组对象。
1
2

# 3.2 复用对象

var t = {} // 每次循环都会创建一个新对象。
for (var i = 0; i < 10; i++) {
  // var t = {};// 每次循环都会创建一个新对象。
  t.age = 19
  t.name = '123'
  t.index = i
  console.log(t)
}
t = null //对象如果已经不用了,那就立即设置为null;等待垃圾回收。
1
2
3
4
5
6
7
8
9

# 3.3 循环中避免放置函数表达式

  • 在循环中最好也别使用函数表达式。
for (var k = 0; k < 10; k++) {
  var t = function(a) {
    // 创建了10次  函数对象。
    console.log(a)
  }
  t(k)
}
1
2
3
4
5
6
7
  • 推荐用法。
function t(a) {
    console.log(a)
}
for (var k = 0; k < 10; k++) {
    t(k)
}
t = null
1
2
3
4
5
6
7