Open ShirlyChenLaLaLa opened 5 years ago
应用程序不再需要占用内存的时候,由于某些原因,内存没有被操作系统或可用内存池回收。
function foo(arg) { bar = "this is a hidden global variable"; }
function foo() { this.variable = "potential accidental global"; }
JavaScript对未声明变量的处理方式:在全局对象上创建该变量的引用(即全局对象上的属性,不是变量,因为它能通过delete删除)。 未声明的变量缓存大量的数据,会导致这些数据只有在窗口关闭或重新刷新页面时才能被释放。与全局变量相关的增加内存消耗的一个主因是缓存。高内存消耗导致缓存突破上限,因为缓存内容无法被回收。
解决方法: 在JavaScript文件中添加'use strict',开启严格模式,可以有效地避免上述问题。
老版本的 IE 是无法检测 DOM 节点与 JavaScript 代码之间的循环引用,会导致内存泄漏。如今,现代的浏览器(包括 IE 和 Microsoft Edge)使用了更先进的垃圾回收算法,已经可以正确检测和处理循环引用了。换言之,回收节点内存时,不必非要调用 removeEventListener 了。
被遗忘的计时器示例:
var someResource = getData(); setInterval(function() { var node = document.getElementById('Node'); if(node) { // 处理 node 和 someResource node.innerHTML = JSON.stringify(someResource)); } }, 1000);
node 对象一旦被删除,而计时器却仍然没被回收,同时someResource存储了大量数据的话,也无法被回收。
闭包:当一个函数A返回一个内联函数B,即使函数A执行完,函数B也能访问函数A作用域内的变量
function foo(message) { function closure() { console.log(message) }; return closure; } // 使用 var bar = foo("hello closure!"); bar()// 打印 'hello closure!'
在函数foo内创建的函数closure对象是不能被回收掉的,因为它被全局变量bar引用,处于一直可访问状态。通过执行bar()可以打印出hello closure!。如果想释放掉可以将bar = null。
如果创建的内部函数没有被其他对象引用,不管内部函数是否引用外部函数的变量和函数,在外部函数执行完,对应变量对象便会被销毁。
同样,我们可以使得闭包函数作用域中没有包含函数对应的变量对象。对于以上例子就是不引用message,直接console.log('haha')。
console.log('haha')
背景:在JavaScript中,DOM操作是非常耗时的。因为JavaScript/ECMAScript引擎独立于渲染引擎,而DOM是位于渲染引擎,相互访问需要消耗一定的资源。如Chrome浏览器中DOM位于WebCore,而JavaScript/ECMAScript位于V8中。假如将JavaScript/ECMAScript、DOM分别想象成两座孤岛,两岛之间通过一座收费桥连接,过桥需要交纳一定“过桥费”。JavaScript/ECMAScript每次访问DOM时,都需要交纳“过桥费”。因此访问DOM次数越多,费用越高,页面性能就会受到很大影响。 一篇非常好的文章
在IE<8版本中,JScript垃圾回收器仅管理JScript对象生命周期而不会管理DOM对象的(即DOM对象有自己的垃圾回收器)。因此JScript回收器不会解除掉DOM对象与Jscript对象之间的相互引用,这从而导致内存泄露。 在IE6中,循环引用只在IE浏览器程序退出时才会被解除,而在IE7中,离开当前页面时,才会解除页面中的循环引用。IE8修复该问题,JScript垃圾回收器会将引用的DOM对象视为JScript对象,从而避免循环引用不能被解除的问题(注:这里循环引用解除是指浏览器自动解除循环引用)。
在IE9把BOM和DOM对象转换为真正的js对象。
<html> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Dom-Leakage</title> </head> <body> <input type="button" value="remove" class="remove"> <input type="button" value="add" class="add"> <div class="container"> <pre class="wrapper"></pre> </div> <script> // 因为要多次用到<pre>节点,将其缓存到本地变量wrapper中, var wrapper = document.querySelector('.wrapper'); var counter = 0; document.querySelector('.remove').addEventListener('click', function () { document.querySelector('.container').removeChild(wrapper); }, false); document.querySelector('.add').addEventListener('click', function () { wrapper.appendChild(document.createTextNode('\t' + ++counter + ':a new line text\n')); }, false); </script> </body> </html>
为了减少DOM访问次数,一般情况下,当需要多次访问同一个DOM方法或属性时,会将DOM引用缓存到一个局部变量中。但如果在执行某些删除、更新操作后,可能会忘记释放掉代码中对应的DOM引用,这样会造成DOM内存泄露。
因此应该做如下修改
// 因为要多次用到<pre>节点,将其缓存到本地变量wrapper中, var wrapper = document.querySelector('.wrapper'); var counter = 0; document.querySelector('.remove').addEventListener('click', function () { document.querySelector('.container').removeChild(wrapper); wrapper = null;//在执行删除操作时,将wrapper对pre节点的引用释放掉 }, false); document.querySelector('.add').addEventListener('click', function () { wrapper.appendChild(document.createTextNode('\t' + ++counter + ':a new line text\n')); }, false);
Js 如何避免内存泄露(笔记)
内存泄露的定义
应用程序不再需要占用内存的时候,由于某些原因,内存没有被操作系统或可用内存池回收。
常见的内存泄露
1. 意外的全局变量
JavaScript对未声明变量的处理方式:在全局对象上创建该变量的引用(即全局对象上的属性,不是变量,因为它能通过delete删除)。 未声明的变量缓存大量的数据,会导致这些数据只有在窗口关闭或重新刷新页面时才能被释放。与全局变量相关的增加内存消耗的一个主因是缓存。高内存消耗导致缓存突破上限,因为缓存内容无法被回收。
解决方法: 在JavaScript文件中添加'use strict',开启严格模式,可以有效地避免上述问题。
2. 被遗忘的计时器或回调函数
老版本的 IE 是无法检测 DOM 节点与 JavaScript 代码之间的循环引用,会导致内存泄漏。如今,现代的浏览器(包括 IE 和 Microsoft Edge)使用了更先进的垃圾回收算法,已经可以正确检测和处理循环引用了。换言之,回收节点内存时,不必非要调用 removeEventListener 了。
被遗忘的计时器示例:
node 对象一旦被删除,而计时器却仍然没被回收,同时someResource存储了大量数据的话,也无法被回收。
3. 闭包
闭包:当一个函数A返回一个内联函数B,即使函数A执行完,函数B也能访问函数A作用域内的变量
在函数foo内创建的函数closure对象是不能被回收掉的,因为它被全局变量bar引用,处于一直可访问状态。通过执行bar()可以打印出hello closure!。如果想释放掉可以将bar = null。
如果创建的内部函数没有被其他对象引用,不管内部函数是否引用外部函数的变量和函数,在外部函数执行完,对应变量对象便会被销毁。
同样,我们可以使得闭包函数作用域中没有包含函数对应的变量对象。对于以上例子就是不引用message,直接
console.log('haha')
。4. DOM泄露 (一般来说是IE<8)
背景:在JavaScript中,DOM操作是非常耗时的。因为JavaScript/ECMAScript引擎独立于渲染引擎,而DOM是位于渲染引擎,相互访问需要消耗一定的资源。如Chrome浏览器中DOM位于WebCore,而JavaScript/ECMAScript位于V8中。假如将JavaScript/ECMAScript、DOM分别想象成两座孤岛,两岛之间通过一座收费桥连接,过桥需要交纳一定“过桥费”。JavaScript/ECMAScript每次访问DOM时,都需要交纳“过桥费”。因此访问DOM次数越多,费用越高,页面性能就会受到很大影响。 一篇非常好的文章
在IE<8版本中,JScript垃圾回收器仅管理JScript对象生命周期而不会管理DOM对象的(即DOM对象有自己的垃圾回收器)。因此JScript回收器不会解除掉DOM对象与Jscript对象之间的相互引用,这从而导致内存泄露。 在IE6中,循环引用只在IE浏览器程序退出时才会被解除,而在IE7中,离开当前页面时,才会解除页面中的循环引用。IE8修复该问题,JScript垃圾回收器会将引用的DOM对象视为JScript对象,从而避免循环引用不能被解除的问题(注:这里循环引用解除是指浏览器自动解除循环引用)。
在IE9把BOM和DOM对象转换为真正的js对象。
为了减少DOM访问次数,一般情况下,当需要多次访问同一个DOM方法或属性时,会将DOM引用缓存到一个局部变量中。但如果在执行某些删除、更新操作后,可能会忘记释放掉代码中对应的DOM引用,这样会造成DOM内存泄露。
因此应该做如下修改