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,一个是oao2 = "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 和 someResourcenode.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 任务管理器

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

Performance工具

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

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

Profiles工具

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

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 按钮。过一阵停止记录看结果:

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

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

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

延伸阅读

  • 4类 JavaScript 内存泄漏及如何避免
  • **chrome浏览器开发工具指南 **
  • **阮一峰:JavaScript 内存泄漏教程 **

转载于:https://juejin.im/post/5cd8e66d51882568cb68b03e

JavaScript内存管理相关推荐

  1. javaScript 内存管理机制

    大家好,今天分享的主题为 JavaScript 内存管理机制,本次分享将从以下三部分进行讲述: js 内存管理与 js 垃圾 常见的 GC 算法 V8 引擎的垃圾回收 js 内存管理与 js 垃圾 关 ...

  2. JavaScript内存管理——优化内存占用

    使用具备垃圾收集机制的语言编写程序,开发人员一般不必操心内存管理的问题.但是,JavaScript在进行内存管理及垃圾收集时面临的问题还是有点与众不同.其中最主要的一个问题,就是分配给Web浏览器的可 ...

  3. 三分钟快速理解javascript内存管理

    javascript中具有垃圾自动回收机制(Garbage Collection),也就是执行环境会负责管理代码执行过程中使用的内存,在开发过程中就可以不考虑内存的分配,以及无用内存释放的问题.但是触 ...

  4. JavaScript内存管理机制以及四种常见的内存泄漏解析

    转自:http://geek.csdn.net/news/detail/238898 原文:How JavaScript works: memory management + how to handl ...

  5. 深入浅出 JavaScript 内存管理,垃圾回收

    简介 本篇文章讲解JavaScript 中垃圾回收机制,内存泄漏,结合一些常遇到的例子,相信各位看完后,会对JS 中垃圾回收机制有个深入的了解. 我的github,欢迎 star 内存生命周期 首先, ...

  6. JavaScript性能优化【上】-- 内存管理、垃圾回收

    JavaScript 内存管理 内存为什么需要管理 function fn () {arrList = []arrList[100000] = 'lg is a coder'}fn() // 内存泄露 ...

  7. JavaScript内存优化

    相对C/C++ 而言,我们所用的JavaScript 在内存这一方面的处理已经让我们在开发中更注重业务逻辑的编写.但是随着业务的不断复杂化,单页面应用.移动HTML5 应用和Node.js 程序等等的 ...

  8. 无法读取内存属于错误吗_深入了解 JavaScript 内存泄露

    用户一般不会在一个 Web 页面停留比较久,即使有一点内存泄漏,重载页面内存也会跟着释放.而且浏览器也有自动回收内存的机制,所以我们前端其实并没有像 C.C++ 这类语言一样,特别关注内存泄漏的问题. ...

  9. JavaScript 内存机制(前端同学进阶必备)

    JavaScript 内存机制(前端同学进阶必备) 简介 每种编程语言都有它的内存管理机制,比如简单的C有低级的内存管理基元,像malloc(),free().同样我们在学习JavaScript的时候 ...

最新文章

  1. spring security oauth rce (cve-2016-4977) 漏洞分析
  2. python 编程入门-Python编程入门电子书教程,看这几个就够了
  3. 迷失在小镇上的日记(16)
  4. wxWidgets:wxRichTextFieldTypeStandard类用法
  5. 只需几分钟跟小猫学前端(内含视频教程):nodejs基础之用express、ejs、mongdb建设简单的网站...
  6. ORACLE rac集群概念和原理
  7. 刚刚发现的 xaml里面颜色的定义方式
  8. hdfs合并小文件测试
  9. Auto Highlight for Mac(Safari文本自动高亮插件)
  10. selenium元素定位——下拉选择框
  11. RPC规范接口实现模块Flask-JSONRPC
  12. JavaWeb(二)Servlet和JSP简介
  13. mysql2008安装失败_sql server 2008为什么会安装失败 sql2008安装失败解决办法
  14. html 不显示undefine,undefined是阴性的意思吗
  15. minio-operator部署minio服务,并用Java客户端访问minio
  16. 无线鼠标插上去没反应
  17. SpringCloud学习(十八):Config分布式配置中心的介绍与搭建
  18. Chromium为视频标签 video 全屏播放的过程分析
  19. 使用JAVA面向对象语言,完成五子棋应用功能设计
  20. php版葫芦侠签到,葫芦侠三楼一键签到工具

热门文章

  1. idea常用快捷键设置
  2. 定位html中的背景图,关于背景图的定位和透明度问题(HTML+CSS笔记)
  3. python 怎么判断文件存在哪里_Python判断文件和文件夹是否存在的方法
  4. UCSD本科数学计算机专业前景如何,2020年UCSD计算机工程排名真该小心来考查
  5. finalshell连接失败解决方法_iPhone热点连不上?教你网络连接失败或断线的解决办法...
  6. as3中splice和slice的用法
  7. C#窗体在任务栏对窗体放大或缩小
  8. socket 客户端-服务器的创建--day28
  9. 在内存中创建临时表和表变量
  10. 使用FlashFXP V3.8烈火汉化绿色版软件连接Linux