加入收藏 | 设为首页 | 会员中心 | 我要投稿 温州站长网 (https://www.0577zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

中高级前端必须了解的JS中的内存管理

发布时间:2019-07-04 12:44:11 所属栏目:优化 来源:王爷科技
导读:副标题#e# 前言 像C语言这样的底层语言一般都有底层的内存管理接口,比如 malloc()和free()用于分配内存和释放内存。 而对于JavaScript来说,会在创建变量(对象,字符串等)时分配内存,并且在不再使用它们时自动释放内存,这个自动释放内存的过程称为垃圾回

该对象包含四个字段,单位是字节,含义如下:

  • rss(resident set size):所有内存占用,包括指令区和堆栈。
  • heapTotal:"堆"占用的内存,包括用到的和没用到的。
  • heapUsed:用到的堆的部分。
  • external: V8 引擎内部的 C++ 对象占用的内存。

判断内存泄漏,以heapUsed字段为准。

常见的内存泄露案例

意外的全局变量

  1. function foo() { 
  2.  bar1 = 'some text'; // 没有声明变量 实际上是全局变量 => window.bar1 
  3.  this.bar2 = 'some text' // 全局变量 => window.bar2 
  4. foo(); 

在这个例子中,意外的创建了两个全局变量 bar1 和 bar2

被遗忘的定时器和回调函数

在很多库中, 如果使用了观察者模式, 都会提供回调方法, 来调用一些回调函数。

要记得回收这些回调函数。举一个 setInterval的例子:

  1. var serverData = loadData(); 
  2. setInterval(function() { 
  3.  var renderer = document.getElementById('renderer'); 
  4.  if(renderer) { 
  5.  renderer.innerHTML = JSON.stringify(serverData); 
  6.  } 
  7. }, 5000); // 每 5 秒调用一次 

如果后续 renderer 元素被移除,整个定时器实际上没有任何作用。

但如果你没有回收定时器,整个定时器依然有效, 不但定时器无法被内存回收,

定时器函数中的依赖也无法回收。在这个案例中的 serverData 也无法被回收。

闭包

在 JS 开发中,我们会经常用到闭包,一个内部函数,有权访问包含其的外部函数中的变量。

下面这种情况下,闭包也会造成内存泄露:

  1. var theThing = null; 
  2. var replaceThing = function () { 
  3.  var originalThing = theThing; 
  4.  var unused = function () { 
  5.  if (originalThing) // 对于 'originalThing'的引用 
  6.  console.log("hi"); 
  7.  }; 
  8.  theThing = { 
  9.  longStr: new Array(1000000).join('*'), 
  10.  someMethod: function () { 
  11.  console.log("message"); 
  12.  } 
  13.  }; 
  14. }; 
  15. setInterval(replaceThing, 1000); 

这段代码,每次调用 replaceThing 时,theThing 获得了包含一个巨大的数组和一个对于新闭包 someMethod 的对象。

同时 unused 是一个引用了 originalThing 的闭包。

这个范例的关键在于,闭包之间是共享作用域的,尽管 unused 可能一直没有被调用,但是 someMethod 可能会被调用,就会导致无法对其内存进行回收。

当这段代码被反复执行时,内存会持续增长。

DOM 引用

很多时候, 我们对 Dom 的操作, 会把 Dom 的引用保存在一个数组或者 Map 中。

  1. var elements = { 
  2.  image: document.getElementById('image') 
  3. }; 
  4. function doStuff() { 
  5.  elements.image.src = 'http://example.com/image_name.png'; 
  6. function removeImage() { 
  7.  document.body.removeChild(document.getElementById('image')); 
  8.  // 这个时候我们对于 #image 仍然有一个引用, Image 元素, 仍然无法被内存回收. 

上述案例中,即使我们对于 image 元素进行了移除,但是仍然有对 image 元素的引用,依然无法对齐进行内存回收。

另外需要注意的一个点是,对于一个 Dom 树的叶子节点的引用。

举个例子: 如果我们引用了一个表格中的td元素,一旦在 Dom 中删除了整个表格,我们直观的觉得内存回收应该回收除了被引用的 td 外的其他元素。

但是事实上,这个 td 元素是整个表格的一个子元素,并保留对于其父元素的引用。

这就会导致对于整个表格,都无法进行内存回收。所以我们要小心处理对于 Dom 元素的引用。

如何避免内存泄漏

记住一个原则:不用的东西,及时归还。

  1. 减少不必要的全局变量,使用严格模式避免意外创建全局变量。
  2. 在你使用完数据后,及时解除引用(闭包中的变量,dom引用,定时器清除)。
  3. 组织好你的逻辑,避免死循环等造成浏览器卡顿,崩溃的问题。

(编辑:温州站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读