深刻浅出 JavaScript 内存管理,垃圾回收

简介

本篇文章讲解JavaScript 中垃圾回收机制,内存泄漏,结合一些常遇到的例子,相信各位看完后,会对JS 中垃圾回收机制有个深刻的了解。node

个人github,欢迎 stargit

内存生命周期

首先,无论什么程序语言,内存生命周期基本是一致的:github

  • 分配你所须要的内存
  • 使用分配到的内存(读、写)
  • 不须要时将其释放归还

 在全部语言中第一和第二部分都很清晰。最后一步在低级语言中(C语言等)很清晰,可是在像JavaScript 等高级语言中,这一步是隐藏的、透明的。由于JavaScript 具备自动垃圾收集机制(Garbage collected )。在编写 JS 时,不须要关心内存使用问题,所需内存分配以及无用内存的回收彻底实现了自动管理。web

内存泄漏

内存泄漏(memory leaks),什么状况下回致使内存泄漏?能够简单理解为有些代码原本要被回收的,但没有被回收,还一直占用着操做系统内存,从而越积越多,最终会致使内存泄漏(能够理解为,内存满了,就溢出了)。算法

管理内存(Memory Management)

分配给web浏览器的可用内存数量一般要比分配给桌面应用程序少。这样作的目的主要是处于安全方面考虑,目的是防止运行JS 的网页耗尽所有系统内存而致使系统崩溃。内存限制问题不只会影响给变量分配内存,同时还会影响调用栈以及在一个线程中可以同时执行的语句数量。chrome

所以,确保占用最少的内存可让页面得到更好的性能。而优化内存占用的最佳方式,就是为执行中的代码只保存必要的数据。一旦数据再也不有用,最好经过将其值设置为 null 来释放其引用。这个方法叫作解除引用。这一作法适用于大多数的全局变量和全局对象的属性。局部变量会在他们离开执行环境时自动被解除引用。浏览器

解除一个值的引用并不意味着自动回收改值所占用的内存。解除引用的真正做用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。安全

标记清除(Mark and Sweep)

一般,垃圾收集器(garbage collector)在运行时候会给储存在内存中的全部变量都加上标记。而后,它会去掉环境中的变量以及被环境中的变量引用的变量的标记。而在此以后再被加上标记的变量将被视为准备删除的变量,缘由是环境中的变量已经没法访问到这些变量了。最后,垃圾收集器完成内存清除的工做。ide

那标记清除具体是如何呢?有如下几种算法:函数

  • 在JavaScript 中,全局变量(Global)和window 对象会一直存在,不会被垃圾收集器回收;
  • 递归所用到的全部(包括变量和方法),都不会被回收;
  • 全部没有被标记为“活跃(active)”的,都会被认为是垃圾,收集器释放会回收垃圾,并把内存还给操做系统。

例子:

例一:

var n = 123; 
// 给数值变量分配内存

var s = "azerty"; 
// 给字符串分配内存

// 给对象及其包含的值分配内存
var o = {
  a: 1,
  b: null
};

// 给函数(可调用的对象)分配内存
function f(a){
  return a + 2;
}

例二:

function foo(arg) {
  // 此处bar 是全局变量,window.bar 能够访问,因此也不会被回收
  bar = "this is a hidden global variable";
} 

function foo() {
  // 此处this 表明 window
  this.variable = "potential accidental global";
}

例三:

var someResource = getData();
setInterval(function() {
  var node = document.getElementById('Node');
  if(node) {
    node.innerHTML = JSON.stringify(someResource));
  }
}, 1000);

// 上面这段代码,定时器setInterval 和 someResource 一直存在,不会被回收。能够改为下面代码

var element = document.getElementById('button');

function onClick(event) {
    element.innerHtml = 'text';
}

element.addEventListener('click', onClick);
// 手动移除事件监听器和变量
element.removeEventListener('click', onClick);
element.parentNode.removeChild(element);

例四:

var intervalId = null, params;

function createChunks() {
  var div, foo, i, str;
  for (i = 0; i < 20; i++) {
    div = document.createElement("div");
    str = new Array(1000000).join('x');
      foo = {
        str: str,
        div: div
      };
      div.foo = foo;
  }
}

function start() {
  if (intervalId) {
    return;
  }
  intervalId = setInterval(createChunks, 1000);
}

function stop() {
  if (intervalId) {
    // 清除定时器
    clearInterval(intervalId);
  }
  // 清除变量
  intervalId = null;
}

连接观察垃圾回收是怎么工做的—Google: Watching the GC work

图片描述

在上面图片中,能够观察到,点击 start 按钮,内存和节点数暴增,当点击stop 时,垃圾收集器回收了这些定时器、变量等,从而释放了内存。

上期博客

个人github,欢迎star

相关文章
相关标签/搜索