内存泄露

在javascript中,我们非常少去关注内存的管理。

我们创建变量,使用变量,浏览器关注这些底层的细节都显得非常正常。

可是当应用程序变得越来越复杂而且ajax化之后,或者用户在一个页面停留过久,我们可能须要去注意一些问题。如一个浏览器花费了1G以上的内存,而且在不断的添加。

这些问题经常都是由于内存泄露引起。

Javascript 内存泄露

这个javascript内存管理的核心概念就是具不具有可达性的概念。

1 一个明显的对象集合将会被觉得是可达的:这些对象是被知道的像roots一样。

包含那些全部的对象在堆栈中个地方被引用(包含,全部的局部变量。正在被调用的方法的中的參数),以及不论什么的全局变量。

2 对象保存在内存中,他们是能够到达的从roots 对象,通过一个引用货者一个引用链。

这里有一个GC 垃圾回收器在浏览器中。用来清楚没实用的对象在内存中。

垃圾回收example

function Menu(title) {
this.title = title
this.elem = document.getElementById('id')
} var menu = new Menu('My Menu') document.body.innerHTML = '' // (1) menu = new Menu('His menu') // (2)

来看一下内存结构:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

在step(1) 中,Body.innerHTML 被清除掉,所以它的子节点也会被删除,由于他们不再被关联。

可是这个元素#id 是一个例外,他是是被 menu.elem 关联着,所以该对象会一直存在内存中,当然 。假设你检查他的parentNode, 将会得到一个null值。

注意:个别的Dom元素 能够会保存在内存中即使他们的parent 被移除了。

在step(2) 中,引用window.menu 被定义。所以之前的 menu由于不再被关联。它将会自己主动被移除通过浏览器的GC。

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

循环引用集合

闭包常常会导致循环引用,比如:

function setHandler() {

  var elem = document.getElementById('id')

  elem.onclick = function() {
// ...
} }

在这里,这个DOM 元素直接引用匿名function通过onclick。

而且这个function引用了elem元素通过外部的词法环境。

( 这里多说一点,关于[[Scope]]是function的内部属性。在创建function的时候。会将外部函数的词法环境增加到[[Scope]]中,这里涉及到javascript的作用域问题。)

这种内存结构一样会出现即使这个处理函数内部没有不论什么的代码。特别的一些方法如addEventListener/attachEvent 也会在内部创建一个引用。

在这个处理函数中通常进行清除。当这个elem死亡的时候。

function cleanUp() {
var elem = document.getElementById('id')
elem.parentNode.removeChild(elem)
}

调用clearUp删除元素从Dom 中。

这里依然存在一个引用,LexialEnvironment.elem ,可是这里没有了嵌套的functions,所以 LexialEnvironment 是能够回收的。

在这之后。elem 变成没有关联的而且和他的handlers一起被回收。

内存泄露

内存泄露主要发生当一些浏览器因为一些问题不可以移除没实用的对象从内存中。

这发生可能是因为一些原因,如浏览器的Bugs,浏览器的扩展问题,或多或少,我们自己的代码错误。

IE 8 下面 DOM-JS 内存泄露

IE8 之前的浏览器不能对DOM和javascript之间的循环引用进行清理。

这个问题相对更加的严重在ie6 windows xp sp3 之前的版本号

由于内存没法释放在页面卸载之前。

所以 setHandler 泄露在ie 8 之前的浏览器,elem 和这些闭包没办法清除。

function setHandler() {
var elem = document.getElementById('id')
elem.onclick = function() { /* ... */ }
}

不不过DOM 元素。包含XMLHttpRequest 或者其他COM 对象。都会存在此现象。

在IE下用来打破循环引用的方法:

我们定义了elem = null,所以这个处理函数不再关联到DOM 元素,这个循环自然打破。

XmlHttpRequest 内存管理和泄露

以下的代码在i9以下浏览器内存泄露:

var xhr = new XMLHttpRequest() // or ActiveX in older IE

xhr.open('GET', '/server.url', true)

xhr.onreadystatechange = function() {
if(xhr.readyState == 4 && xhr.status == 200) {
// ...
}
} xhr.send(null)

看一下内存结构:

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt="">

这个异步xmlHttpRequest对象一直被浏览器追踪,由于有一个内部的引用关联到它。

当这个请求结束之后,这个引用就会被删除,所以xhr 变成不可关联对象。可是ie9下面的浏览器不是这么做的。

幸运的是,要修复这个Bug非常easy。我们须要删除这个xhr 从这个闭包中而且使用它用this在这个处理函数中。

例如以下:

var xhr = new XMLHttpRequest()

xhr.open('GET', 'jquery.js', true)

xhr.onreadystatechange = function() {
if(this.readyState == 4 && this.status == 200) {
document.getElementById('test').innerHTML++
}
} xhr.send(null)
xhr = null
}, 50)

这样就没有了循环引用。

setInterval/setTimeout

在使用setTimeout/setInterval 也会存在内部引用而且被追踪知道结束。然后clear up.

对于setInterval 这个结束发生在 clearInterval中,这个可能会导致内存泄露当这种方法实际什么也 没做,可是这个interval却没有被清除。

内存泄露的大小

内存泄露的数据结构的size可能不大。

可是这个闭包会导致外部函数的全部的变量遗留下来,当这个内部函数是活动的时候。

所以,你能够想象,你创建了一个function,并且当中一个变量包括了一个大的字符串。

function f() {
var data = "Large piece of data, probably received from server" /* do something using data */ function inner() {
// ...
} return inner
}

While the function inner function stays in memory, then the LexicalEnvironment with a large variable inside will hang in memory until the inner function is alive.

其实,这可能没有泄露。很多的fucntions 可能会被创建由于一些合理的原因。比方,对于每个请求,并不清干净。由于他们是一些处理函数或者其他什么。

假设这个data 只被使用在外部函数。我们能够使它作废在外部方法中。

function f() {
var data = "Large piece of data, probably received from server" /* do something using data */ function inner() {
// ...
} data = null return inner
}

如今。

这个data 依然保留在内存中作为一个词法环境的一个属性。只是它不再须要去占用太多的空间。

jQuery 内存泄露和避免方式

jQuery 使用 $.data 去避免ie 6 7 内存泄露。不幸运的是。它导致了一些新的 jQuery 特殊的内存泄露。

这个核心原理关于$.data是,不论什么的javascript实体被限制去读取一个元素使用例如以下的方式

// works on this site cause it's using jQuery

$(document.body).data('prop', 'val') // set
alert( $(document.body).data('prop') ) // get

jQuery $(elem).data(prop,val) 依照例如以下步骤:

1 元素获取一个唯一的标记假设它不存在的话:

elem[ jQuery.expando ] = id = ++jQuery.uuid  // from jQuery source

2 data 被设置到一个特殊的对象 jQuery.cache:

jQuery.cache[id]['prop'] = val

当这个date从一个元素中被读取:

1 这个元素的唯一标示会被返回:id = elem[jQuery.expando]

2 这个data 会被读取从jQuery.cache[id]

jQuery设置这个api的目的就是为了让DOM元素不再直接引用Javascript元素。

它使用了一个数量,可是非常安全。

这个data 保存在jQuery.cache中。

内部事件处理函数相同使用$.data API。

同一时候也造成了还有一方面的影响,一个元素不能被移除从DOM中使用 本地的调用。

例如以下代码造成了内存泄露在全部的浏览器中:

$('<div/>')
.html(new Array(1000).join('text')) // div with a text, maybe AJAX-loaded
.click(function() { })
.appendTo('#data') document.getElementById('data').innerHTML = ''

这个泄露的发生由于elem 被removeed 通过清除 parent 的innerHTML .可是这个data依然保存在jQuery.cache中。

更重要的是,这个事件处理函数引用elem,所以这个事件处理函数和elem保留在内存中和整个闭包。

一个简单的泄露样例

function go() {
$('<div/>')
.html(new Array(1000).join('text'))
.click(function() { })
}

这个样例的问题在于,这个元素被创建了。可是没有使用。

所以在这个函数定义之后,这个引用就消失了。 可是这个jQuery.cache中依然是存在的。

----------------------------------------------------------------------------

原文地址:http://javascript.info/tutorial/memory-leaks

[ Javascript ] 内存泄露以及循环引用解析的更多相关文章

  1. 使用gc、objgraph干掉python内存泄露与循环引用!

    Python使用引用计数和垃圾回收来做内存管理,前面也写过一遍文章<Python内存优化>,介绍了在python中,如何profile内存使用情况,并做出相应的优化.本文介绍两个更致命的问 ...

  2. 4类 JavaScript 内存泄露及如何避免

    原文:4 Types of Memory Leaks in JavaScript and How to Get Rid Of Them笔记:涂鸦码龙 译者注:本文并没有逐字逐句的翻译,而是把我认为重要 ...

  3. JavaScript内存泄露,闭包内存泄露如何解决

    本文原链接:https://cloud.tencent.com/developer/article/1340979 JavaScript 内存泄露的4种方式及如何避免 简介 什么是内存泄露? Java ...

  4. 常见的几种JavaScript内存泄露

    总结一下常见的几种JavaScript内存泄露: 1.意外的全局变量 全局变量属于window对象,所以只会随着window销毁才会销毁. 2.console.log() conaole.log()函 ...

  5. Javascript内存泄露

    在过去一些的时候,Web开发人员并没有太多的去关注内存泄露问题.那时的页面间联系大都比较简单,并主要使用不同的连接地址在同一个站点中导航,这样的设计方式是非常有利于浏览器释放资源的.即使Web页面运行 ...

  6. (译)JavaScript内存泄露

    译者前言 原文地址:Memory leaks 最近简单了解了下JavaScript的闭包和垃圾回收机制(GC),这中间也不得不接触内存泄露这个概念.然后不小心找到了这篇文章,看下来后理解了不少东西,于 ...

  7. 【翻译】JavaScript内存泄露

    原文地址:http://javascript.info/tutorial/memory-leaks#tools 我们在进行JavaScript开发时,很少会考虑内存的管理.JavaScript中变量的 ...

  8. JavaScript 内存泄露以及如何处理

    一.前言 一直有打算总结一下JS内存泄露的方面的知识的想法,但是总是懒得提笔. 富兰克林曾经说过:懒惰,像生鏽一样,比操劳更能消耗身体,经常用的钥匙总是亮闪闪的.安利一下,先起个头. 二.内存声明周期 ...

  9. [转]常见的JavaScript内存泄露

    什么是内存泄露 内存泄漏指由于疏忽或错误造成程序未能释放已经不再使用的内存.内存泄漏并非指内存在物理上的消失,而是应用程序分配某段内存后,由于设计错误,导致在释放该段内存之前就失去了对该段内存的控制, ...

随机推荐

  1. Python 1:环境搭建及运行第一个程序

    在计算机控制面板中找到系统,然后点击高级系统设置,再点击环境变量,把安装的python.exe的目录复制到下面系统变量的path栏最后面,复制前加个分号以隔开前面的变量,然后按3次确定即可退出配置.具 ...

  2. bug,实现类未找到service

  3. 5.26 Quartz任务调度图解2

  4. PHP获取远程和本地文件信息(汇总)

    1.PHP filesize() 函数filesize() 函数返回指定文件的大小.若成功,则返回文件大小的字节数.若失败,则返回 false 并生成一条 E_WARNING 级的错误. 但是只能获取 ...

  5. SqlSever2005 一千万条以上记录分页数据库优化经验总结【索引优化 + 代码优化】

    对普通开发人员来说经常能接触到上千万条数据优化的机会也不是很多,这里还是要感谢公司提供了这样的一个环境,而且公司让我来做优化工作.当数据库中的记录不超过10万条时,很难分辨出开发人员的水平有多高,当数 ...

  6. SLAM:使用G2O-ORB-SLAM(编译)

    前言: 没有新雪,看看自己所做的事情,有没有前人做过.果然,EKF_SLAM的版本出现了Android版本的OpenEKFMonoSLAM, G2O-ORB SLAM也出现了VS2012版本. 一.S ...

  7. 卸载pycharm再重新安装后,找不到第三方库

    遇到的问题: 看到pycharm出了新的版本,手痒把旧的版本卸载,然后安装了最新的版本,然后问题就来了. 之前通过PIP命令安装的第三方库,import的时候都报错,找不到模块.既然以前能正常使用,现 ...

  8. 基于 react-navigation 父子组件的跳转链接

    1.在一个页面中中引入一个组件,但是这个组件是一个小组件,例如是一个cell,单独的每个cell都是需要点击有链接跳转的,这个时候通常直接使用 onPress 的跳转就会不起作用 正确的处理方法是,在 ...

  9. vue.js的ajax和jsonp请求

    首先要声明使用ajax 在 router下边的 Index.js中 import VueResource from 'vue-resource'; Vue.use(VueResource); ajax ...

  10. Uoj #274. 【清华集训2016】温暖会指引我们前行 LCT维护边权_动态最小生成树

    Code: 行#include<bits/stdc++.h> #define ll long long #define maxn 1000000 #define inf 100000000 ...