[转]JS的内存泄露处理
问题:
1、给DOM对象添加的属性是一个对象的引用。范例:
var MyObject = {};
document.getElementByIdx_x('myDiv').myProp = MyObject;
解决方法:
在window.onunload事件中写上: document.getElementByIdx_x('myDiv').myProp = null;
2、DOM对象与JS对象相互引用。范例:
function Encapsulator(element) {
this.elementReference = element;
element.myProp = this;
}
new Encapsulator(document.getElementByIdx_x('myDiv'));
解决方法:
在onunload事件中写上: document.getElementByIdx_x('myDiv').myProp = null;
3、给DOM对象用attachEvent绑定事件。范例:
function doClick() {}
element.attachEvent("onclick", doClick);
解决方法:
在onunload事件中写上: element.detachEvent('onclick', doClick);
4、从外到内执行appendChild。这时即使调用removeChild也无法释放。范例:
var parentDiv = document.createElement_x("div");
var childDiv = document.createElement_x("div");
document.body.appendChild(parentDiv);
parentDiv.appendChild(childDiv);
解决方法:
从内到外执行appendChild:
var parentDiv = document.createElement_x("div");
var childDiv = document.createElement_x("div");
parentDiv.appendChild(childDiv);
document.body.appendChild(parentDiv);
5、反复重写同一个属性会造成内存大量占用(但关闭IE后内存会被释放)。范例:
for(i = 0; i < 5000; i++) {
hostElement.text = "asdfasdfasdf";
}
这种方式相当于定义了5000个属性!
解决方法:
其实没什么解决方法:P~~~就是编程的时候尽量避免出现这种情况咯~~
优化原则:
首先,与其他语言不同,JS的效率很大程度是取决于JS engine的效率。除了引擎实现的优劣外,引擎自己也会为一些特殊的代码模式采取一些优化的策略。例如FF、Opera和Safari的JS引擎,都对 字符串的拼接运算(+)做了特别优化。显然,要获得最大效率,就必须要了解引擎的脾气,尽量迎合引擎的口味。所以对于不同的引擎,所作的优化极有可能是背 道而驰的。
而如果做跨浏览器的web编程,则最大的问题是在于IE6(JScript 5.6)!因为在不打hotfix的情况下,JScript引擎的垃圾回收的bug,会导致其在真实应用中的performance跟其他浏览器根本不在一个数量级上。因此在这种场合做优化,实际上就是为JScript做优化!
所以第一原则就是只需要为IE6(未打补丁的JScript 5.6或更早版本)做优化!
如果你的程序已经优化到在IE6下可以接受的性能,那基本上在其他浏览器上性能就完全没有问题。
因此,注意我下面讲的许多问题在其他引擎上可能完全不同,例如在循环中进行字符串拼接,通常认为需要用Array.join的方式,但是由于 SpiderMonkey等引擎对字符串的“+”运算做了优化,结果使用Array.join的效率反而不如直接用“+”!但是如果考虑IE6,则其他浏 览器上的这种效率的差别根本不值一提。
JS优化与其他语言的优化也仍然有相同之处。比如说,不要一上来就急吼吼的做优化,那样毫无意义。优化的关键,仍然是要把精力放在最关键的地方, 也就是瓶颈上。一般来说,瓶颈总是出现在大规模循环的地方。这倒不是说循环本身有性能问题,而是循环会迅速放大可能存在的性能问题。
所以第二原则就是以大规模循环体为最主要优化对象。
以下的优化原则,只在大规模循环中才有意义,在循环体之外做此类优化基本上是没有意义的。
目前绝大多数JS引擎都是解释执行的,而解释执行的情况下,在所有操作中,函数调用的效率是较低的。此外,过深的prototype继承链或者多 级引用也会降低效率。JScript中,10级引用的开销大体是一次空函数调用开销的1/2。这两者的开销都远远大于简单操作(如四则运算)。
所以第三原则就是尽量避免过多的引用层级和不必要的多次方法调用。
特别要注意的是,有些情况下看似是属性访问,实际上是方法调用。例如所有DOM的属性,实际上都是方法。在遍历一个NodeList的时候,循环 条件对于nodes.length的访问,看似属性读取,实际上是等价于函数调用的。而且IE DOM的实现上,childNodes.length每次是要通过内部遍历重新计数的。(My god,但是这是真的!因为我测过,childNodes.length的访问时间与childNodes.length的值成正比!)这非常耗费。所以 预先把nodes.length保存到js变量,当然可以提高遍历的性能。
同样是函数调用,用户自定义函数的效率又远远低于语言内建函数,因为后者是对引擎本地方法的包装,而引擎通常是c,c++,java写的。进一步,同样的功能,语言内建构造的开销通常又比内建函数调用要效率高,因为前者在JS代码的parse阶段就可以确定和优化。
所以第四原则就是尽量使用语言本身的构造和内建函数。
这里有一个例子是高性能的String.format方法。 String.format传统的实现方式是用String.replace(regex, func),在pattern包含n个占位符(包括重复的)时,自定义函数func就被调用n次。而这个高性能实现中,每次format调用所作的只是一 次Array.join然后一次String.replace(regex, string)的操作,两者都是引擎内建方法,而不会有任何自定义函数调用。两次内建方法调用和n次的自定义方法调用,这就是性能上的差别。
同样是内建特性,性能上也还是有差别的。例如在JScript中对于arguments的访问性能就很差,几乎赶上一次函数调用了。因此如果一个 可变参数的简单函数成为性能瓶颈的时候,可以将其内部做一些改变,不要访问arguments,而是通过对参数的显式判断来处理。
比如:
Java代码
- function sum() {
- var r = 0;
- for (var i = 0; i < arguments.length; i++) {
- r += arguments[i];
- }
- return r;
- }
这个sum通常调用的时候个数是较少的,我们希望改进它在参数较少时的性能。如果改成:
Java代码
- function sum() {
- switch (arguments.length) {
- case 1: return arguments[0];
- case 2: return arguments[0] + arguments[1];
- case 3: return arguments[0] + arguments[1] + arguments[2];
- case 4: return arguments[0] + arguments[1] + arguments[2] + arguments[3];
- default:
- var r = 0;
- for (var i = 0; i < arguments.length; i++) {
- r += arguments[i];
- }
- return r;
- }
- }
其实并不会有多少提高,但是如果改成:
Java代码
- function sum(a, b, c, d, e, f, g) {
- var r = a ? b ? c ? d ? e ? f ? a + b + c + d + e + f : a + b + c + d + e : a + b + c + d : a + b + c : a + b : a : 0;
- if (g === undefined) return r;
- for (var i = 6; i < arguments.length; i++) {
- r += arguments[i];
- }
- return r;
- }
就会提高很多(至少快1倍)。
最后是第五原则,也往往是真实应用中最重要的性能障碍,那就是尽量减少不必要的对象创建。
本身创建对象是有一定的代价的,但是这个代价其实并不大。最根本的问题是由于JScript愚蠢之极的垃圾回收调度算法,导致随着对象个数的增加,性能严重下降(据微软的人自己说复杂度是O(n^2))。
比如我们常见的字符串拼接问题,经过我的测试验证,单纯的多次创建字符串对象其实根本不是性能差的原因。要命的是在对象创建期间的无谓的垃圾回收的开销。而Array.join的方式,不会创建中间字符串对象,因此就减少了那该死的垃圾回收的开销。
因此,如果我们能把大规模对象创建转化为单一语句,则其性能会得到极大的提高!例如通过构造代码然后eval——实际上PIES项目中正在根据这个想法来做一个专门的大规模对象产生器……
备注:
CollectGarbage,是IE的一个特有属性,用于释放内存的
使用方法嘛应该是,将该变量或引用对象,设置为null或delete
然后在进行释放动作
在做CollectGarbage前,要必需清楚的两个必备条件:
引用
- 一个对象在其生存的上下文环境之外,即会失效。
- 一个全局的对象在没有被执用(引用)的情况下,即会失效。
参考:
http://blog.sina.com.cn/s/blog_734a77160100tgey.html
[转]JS的内存泄露处理的更多相关文章
- Js内存泄露问题总结
最近接受了一个Js职位的面试,问了很多Js的高级特性,才发现长时间使用已知的特性进行开发而忽略了对这门语言循序渐进的理解,包括Java我想也是一样,偶尔在Sun官方看到JDK6.0列举出来的new f ...
- js内存泄露的几种情况详细探讨
内存泄露是指一块被分配的内存既不能使用,又不能回收,直到浏览器进程结束.在C++中,因为是手动管理内存,内存泄露是经常出现的事情.而现在流行的C#和Java等语言采用了自动垃圾回收方法管理内存,正常使 ...
- js内存泄露的几种情况
想解决内存泄露问题,必须知道什么是内存泄露,什么情况下出现内存泄露,才能在遇到问题时,逐个排除.这里只讨论那些不经意间的内存泄露. 一.什么是内存泄露 内存泄露是指一块被分配的内存既不能使用,又不能回 ...
- JS魔法堂:再识IE的内存泄露
一.前言 IE6~8除了不遵守W3C标准和各种诡异外,我想最让人诟病的应该是内存泄露的问题了.这阵子趁项目技术调研的机会好好的再认识一回,以下内容若有纰漏请大家指正,谢谢! 目录一大坨! 二.内存泄漏 ...
- 也议 js闭包和ie内存泄露原理
可以, 但小心使用. 闭包也许是 JS 中最有用的特性了. 有一份比较好的介绍闭包原理的文档. 有一点需要牢记, 闭包保留了一个指向它封闭作用域的指针, 所以, 在给 DOM 元素附加闭包时, 很可能 ...
- javascript js 内存泄露
JavaScript 内存泄露 1.什么是闭包.以及闭包所涉及的作用域链这里就不说了. 2.JavaScript垃圾回收机制 JavaScript不需要手动地释放内存,它使用一种自动垃圾回收机制(ga ...
- JS内存泄露常见原因
详细内容请点击 分享的笔记本-前端 开发中,我们常遇见的一些关于js内存泄露的问题,有时候我们常常会找半天找不出原因,这里给大家介绍简单便捷的方法 1.闭包上下文绑定后没有释放: 2.观察者模式在 ...
- 一个JS内存泄露实例分析
在看JS GC 相关的文章时,好几次看到了下面这个设计出来的例子,比较巧妙,环环相扣. var theThing = null; var replaceThing = function () { ...
- js闭包和ie内存泄露原理
也议 js闭包和ie内存泄露原理 可以, 但小心使用. 闭包也许是 JS 中最有用的特性了. 有一份比较好的介绍闭包原理的文档. 有一点需要牢记, 闭包保留了一个指向它封闭作用域的指针, 所以, 在给 ...
随机推荐
- Python学习笔记(二)在线用pip下载第三方包
根据他人的博客,可以发现pip也是可以离线安装已经下载好的包的,具体请参考<pip常用命令>.由于现在还没进展到那个程度,所以本次博客先记录一下我已经学会的东西. 1.pip基本用法 (1 ...
- 20179209《Linux内核原理与分析》安全类实验答疑
实验一 题目 Nmap 配合 Metasploit 进行端口扫描 问题 Nmap怎么配合Metasploit进行端口扫描? 回答 这里的Nmap配合Metasploit进行端口扫描是指在Metaspl ...
- lua解析json
自己写的lua解析json,带容错,如果要是用于格式检查,得修改下.很简单直接贴代码 --------------------------------------------------json解析- ...
- [luogu3413]萌数
[luogu3413]萌数 luogu 考虑数位dp 怎么判断一个数是不是萌数? 只要知道其中某一位和它的前一位相等或者和前一位的前一位相等,那么它就是一个萌数 什么样的数不是萌数? 对于它的每一位都 ...
- sublime 快捷键,左菜单乱码
sublime 快捷键安装 packagecontrol https://packagecontrol.io/installation#st3 import urllib.request,os,has ...
- 关于align-items和align-content的区别和使用场景
最近在研究flex布局,容器中有两个属性,是用来定义crossAxis测轴排列方式的.一开始接触align-items还可以理解感觉不难,后来看到align-content就感觉有点混淆了,特开一篇博 ...
- 【HTTP】初识代理
Web代理(proxy)位于客户端和服务器端之间.HTTP的代理服务器既是Web服务器端又是Web客户端. 1. 代理和网关的对比 代理连接的是两个或者多个使用相同协议的应用程序. 网关连接的是两个或 ...
- Stream computing
stream data 从广义上说,所有大数据的生成均可以看作是一连串发生的离散事件.这些离散的事件以时间轴为维度进行观看就形成了一条条事件流/数据流.不同于传统的离线数据,流数据是指由数千个数据源持 ...
- 【leetcode刷题笔记】Merge k Sorted Lists
Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. 题 ...
- 【leetcode刷题笔记】Triangle
Given a triangle, find the minimum path sum from top to bottom. Each step you may move to adjacent n ...