JavaScript内存管理

栏目: JavaScript · 发布时间: 6年前

内容简介:底层语言,如C,有底层内存管理函数malloc()和free();JavaScript创建的对象不再使用的时候将会自动释放的过程称为垃圾回收,这种回收机制带来了一些问题:开发者们可以决定不关心内存管理。JavaScript 在定义变量时就完成了内存分配。有些函数调用结果是分配对象内存

底层语言,如C,有底层内存管理函数malloc()和free();JavaScript创建的对象不再使用的时候将会自动释放的过程称为垃圾回收,这种回收机制带来了一些问题:开发者们可以决定不关心内存管理。

一、内存的生命周期

JavaScript 的内存分配

1.值的初始化

JavaScript 在定义变量时就完成了内存分配。

var n = 123; // 给数值变量分配内存
var s = "azerty"; // 给字符串分配内存

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

// 给数组及其包含的值分配内存(就像对象一样)
var a = [1, null, "abra"]; 

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

// 函数表达式也能分配一个对象
someElement.addEventListener('click', function(){
  someElement.style.backgroundColor = 'blue';
}, false);
复制代码

2.通过函数调用分配内存

有些函数调用结果是分配对象内存

var d = new Date(); // 分配一个 Date 对象

var e = document.createElement('div'); // 分配一个 DOM 元素
复制代码

有些方法分配新变量或者新对象

var s = "azerty";
var s2 = s.substr(0, 3); // s2 是一个新的字符串
// 因为字符串是不变量,
// JavaScript 可能决定不分配内存,
// 只是存储了 [0-3] 的范围。

var a = ["ouais ouais", "nan nan"];
var a2 = ["generation", "nan nan"];
var a3 = a.concat(a2); 
// 新数组有四个元素,是 a 连接 a2 的结果
复制代码

3.使用值使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。

4.当内存不再需要使用时释放详情请见垃圾回收机制

垃圾回收机制

1.引用在内存管理的环境中,一个对象如果有访问另一个对象的权限(隐式或者显式),叫做一个对象引用另一个对象。例如,一个Javascript对象具有对它原型的引用(隐式引用)和对它属性的引用(显式引用)。

在这里,“对象”的概念不仅特指 JavaScript 对象,还包括函数作用域(或者全局词法作用域)。

2.引用数垃圾回收这是最简单的垃圾收集算法。此算法把“对象是否不再需要”简化定义为“对象有没有其他对象引用到它”。如果没有引用指向该对象(零引用),对象将被垃圾回收机制回收。(相关阅读: 阮一峰:JavaScript 内存泄漏教程

var o = { 
  a: {
    b:2
  }
}; 
// 两个对象被创建,一个作为另一个的属性被引用,另一个被分配给变量o
// 很显然,没有一个可以被垃圾收集


var o2 = o; // o2变量是第二个对“这个对象”的引用

o = 1;      // 现在,“这个对象”的原始引用o被o2替换了

var oa = o2.a; // 引用“这个对象”的a属性
// 现在,“这个对象”有两个引用了,一个是o2,一个是oa

o2 = "yo"; // 最初的对象现在已经是零引用了
           // 他可以被垃圾回收了
           // 然而它的属性a的对象还在被oa引用,所以还不能回收

oa = null; // a属性的那个对象现在也是零引用了
           // 它可以被垃圾回收了
复制代码

3.标记清除法这个算法把“对象是否不再需要”简化定义为“对象是否可以获得”。

这个算法假定设置一个叫做根(root)的对象(在Javascript里,根是全局对象)。定期的,垃圾回收器将从根开始,找所有从根开始引用的对象,然后找这些对象引用的对象……从根开始,垃圾回收器将找到所有可以获得的对象和所有不能获得的对象。

这个算法比前一个要好,因为“有零引用的对象”总是不可获得的,但是相反却不一定,参考“循环引用”。

从2012年起,所有现代浏览器都使用了标记-清除垃圾回收算法。所有对JavaScript垃圾回收算法的改进都是基于标记-清除算法的改进,并没有改进标记-清除算法本身和它对“对象是否不再需要”的简化定义。

二、内存泄漏问题

常见的内存泄漏类型

1.全局变量JavaScript 处理未定义变量的方式比较宽松:未定义的变量会在全局对象创建一个新变量。在浏览器中,全局对象是 window 。

function foo(arg) {
    bar = "这是一个局部变量";
}
//实际情况是
function foo(arg) {
    window.bar = "这实际是一个全局变量";
}
复制代码

或者是

function foo() {
    this.variable = "potential accidental global";
}
// Foo 调用自己,this 指向了全局对象(window)
// 而不是 undefined
foo();
复制代码

注意事项:尽管我们讨论了一些意外的全局变量,但是仍有一些明确的全局变量产生的垃圾。它们被定义为不可回收(除非定义为空或重新分配)。尤其当全局变量用于临时存储和处理大量信息时,需要多加小心。如果必须使用全局变量存储大量数据时,确保用完以后把它设置为 null 或者重新定义。与全局变量相关的增加内存消耗的一个主因是缓存,缓存内容无法被回收。

相关阅读: 命名空间-极客学院 立即执行函数IIFE-MDN 立即执行函数IIFE-伯乐在线

2:计时器或回调函数

var someResource = getData();
setInterval(function() {
    var node = document.getElementById('Node');
    if(node) {
        // 处理 node 和 someResource
        node.innerHTML = JSON.stringify(someResource));
    }
}, 1000);
复制代码

此例说明了什么:与节点或数据关联的计时器不再需要,node 对象可以删除,整个回调函数也不需要了。可是,计时器回调函数仍然没被回收(计时器停止才会被回收)。同时,someResource 如果存储了大量的数据,也是无法被回收的。

观察者代码示例:

var element = document.getElementById('button');
function onClick(event) {
    element.innerHTML = 'text';
}
element.addEventListener('click', onClick);
复制代码

老版本的 IE 是无法检测 DOM 节点与 JavaScript 代码之间的循环引用,会导致内存泄漏。如今,现代的浏览器(包括 IE 和 Microsoft Edge)使用了更先进的垃圾回收算法,已经可以正确检测和处理循环引用了。换言之,回收节点内存时,不必非要调用 removeEventListener 了。

3:脱离 DOM 的引用

var elements = {
    button: document.getElementById('button'),
    image: document.getElementById('image'),
    text: document.getElementById('text')
};
function doStuff() {
    image.src = 'http://some.url/image';
    button.click();
    console.log(text.innerHTML);
    // 更多逻辑
}
function removeButton() {
    // 按钮是 body 的后代元素
    document.body.removeChild(document.getElementById('button'));
    // 此时,仍旧存在一个全局的 #button 的引用
    // elements 字典。button 元素仍旧在内存中,不能被 GC 回收。
}
复制代码

有时,保存 DOM 节点内部数据结构很有用。假如你想快速更新表格的几行内容,把每一行 DOM 存成字典(JSON 键值对)或者数组很有意义。此时,同样的 DOM 元素存在两个引用:一个在 DOM 树中,另一个在字典中。将来你决定删除这些行时, 需要把两个引用都清除

4.闭包

相关阅读:闭包-MDN

Chrome 内存剖析工具

Chrome 任务管理器

JavaScript内存管理

下面两列可以告诉您与页面的内存使用有关的不同信息: 1.Memory 列表示原生内存。DOM 节点存储在原生内存中。 如果此值正在增大,则说明正在创建 DOM 节点。 2.JavaScript Memory 列表示 JS 堆。此列包含两个值。 您感兴趣的值是实时数字(括号中的数字)。 实时数字表示您的页面上的可到达对象正在使用的内存量。 如果此数字在增大,要么是正在创建新对象,要么是现有对象正在增长。

Performance工具

chrome浏览器在57版本后Timeline工具更名为Performance。相关阅读: Performance-chorme官方文档

JavaScript内存管理

Performance 可以检测代码中不需要的内存。在此截图中,我们可以看到潜在的泄漏对象稳定的增长,数据采集期间内存的变化为[102-216M],出现明显的内存泄漏,从图中的增量来看,js的内存泄漏最为明显也是最主要的。

Profiles工具

Profiles工具主要是用来监控和查找浏览器内存问题的工具。相关阅读: Profiles-chorme官方文档

JavaScript内存管理

Profiles工具分为三种类型: 1.Take heap snapshot 保存当前内存快照 2.Record allocation profile 记录一段时间的内存分配和使用情况 3.Record allocation timeline 以时间线记录内存的分配和使用情况

实例:使用 Chrome 发现内存泄漏

以chrome文档中的代码为例:

var x = [];
function createSomeNodes() {
    var div,
        i = 100,
        frag = document.createDocumentFragment();
    for (;i > 0; i--) {
        div = document.createElement("div");
        div.appendChild(document.createTextNode(i + " - "+ new Date().toTimeString()));
        frag.appendChild(div);
    }
    document.getElementById("nodes").appendChild(frag);
}
function grow() {
    x.push(new Array(1000000).join('x'));
    createSomeNodes();
    setTimeout(grow,1000);
}
复制代码

当 grow 执行的时候,开始创建 div 节点并插入到 DOM 中,并且给全局变量分配一个巨大的数组。通过以上提到的 工具 可以检测到内存稳定上升。

找出周期性增长的内存Performance 工具擅长做这些。在 Chrome 中打开例子,打开 Dev Tools ,切换到 Performance,勾选 memory 并点击记录按钮,然后点击页面上的 The Button 按钮。过一阵停止记录看结果:

JavaScript内存管理

两种迹象显示出现了内存泄漏,图中的 Nodes(绿线)和 JS heap(蓝线)。Nodes 稳定增长,并未下降,这是个显著的信号。

JS heap 的内存占用也是稳定增长。由于垃圾收集器的影响,并不那么容易发现。图中显示内存占用忽涨忽跌,实际上每一次下跌之后,JS heap 的大小都比原先大了。换言之,尽管垃圾收集器不断的收集内存,内存还是周期性的泄漏了。

确定存在内存泄漏之后,我们找找根源所在。待补充。。。


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Lean Startup

The Lean Startup

Eric Ries / Crown Business / 2011-9-13 / USD 26.00

更多中文介绍:http://huing.com Most startups fail. But many of those failures are preventable. The Lean Startup is a new approach being adopted across the globe, chan ging the way companies are built and ......一起来看看 《The Lean Startup》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

SHA 加密
SHA 加密

SHA 加密工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换