我在深圳,QQ上认识一个广西女孩蓝瘦香菇视频老乡的有三个月了,她好几次叫我过去她那边东莞玩,然后呢我也想去,

js的闭包和回调到底怎么才会造成真的内存泄漏呢?_问答_ThinkSAAS
js的闭包和回调到底怎么才会造成真的内存泄漏呢?
js的闭包和回调到底怎么才会造成真的内存泄漏呢?
最近看了一些分享的js内存泄漏典型案例,感觉这种代码以前一直写,现在不留意的话也一直这么写,到现在发现写代码的时候老怕闭包和回调了,在前端出现泄露的话问题还不算非常严重,但如果后端使用nodejs写出现内存泄露的话,这可会让服务崩溃。
为了严谨性,到这里问下各位技术达人。
//一下是angularjs的controller模拟
function($scope){
$scope.todo1 = todo1;
$scope.todo2 = todo2;
$scope.todoAll = {
todo1 : todo1..
像这样的回调其实就是一种闭包,那么这个时候会造成内存泄露吗?
function foo($scope){
var text = 'you click me!!!';
var clickHanlder = function(){
alert(text);
$scope.clickHanlder = clickH
var $a = {}
$a.clickHanlder();
//这里的text被提到全局的作用域了,这种算内存泄露吗?如果$a是DOM呢?
var gloablObject = {}
var addCallback = function(hanlder){
//这样的闭包回调有问题吗?
var value = function(){
temp = new Value(true);
temp = new Value();
hanlder(value);
addCallback(function(value){
//1 这里有问题吗?
globalObject.ref =
//2 这里呢?
globalObject.ref = function(nv){
alert(nv.todo(value));
//下面的代码大致模拟一下angularjs的写法
fn(function($add){
var pro1 = 'pro1';
var pro2 = 'pro2';
return function(){
$add(pro1,pro2);
类似这些写法很经常,现在遇到闭包和回调我都分不出我写的代码会不会造成内存泄露了,js坑很大啊!!
兄弟,不必担心。
首先,能导致内存泄漏的一定是引用类型的变量,比如函数和其他自定义对象。而值类型的变量是不存在内存泄漏的,比如字符串、数字、布尔值等。
因为值类型是靠复制来传递的,而引用类型是靠类似c语言中的指针来传递的。
可以认为一个引用类型的变量就是一个指向某个具体的内存地址的指针。
当我们用js代码创建一个引用类型的时候(以下简称对象),js引擎会在内存中开辟一块空间来存放数据,并把指针引用交给那个变量。内存是有限的,js引擎必须保证当开辟的对象没用的时候,把所分配的内存空间释放出来,这个过程叫做垃圾回收,负责回收的叫做垃圾回收器(GC)。
OK,内存泄漏是指我们已经无法再通过js代码来引用到某个对象,但垃圾回收器却认为这个对象还在被引用,因此在回收的时候不会释放它。导致了分配的这块内存永远也无法被释放出来。如果这样的情况越来越多,会导致内存不够用而系统崩溃。
不可控的东西才是最可怕的!
最经典的例子就是外部我们不可控的引用。比如说IE6中dom对象引用了js对象,而dom对象在某个时刻被移除掉了,但js引擎不知道它被移除掉,还傻傻的保留着引用呢,就不会把js对象释放。(ie7+改善很多了,我不是黑IE)
然后就是闭包中的引用了。咱们使用闭包的目的,就是要保存内部变量的状态以便我们哪个时候去通过闭包使用它作用域内的变量。
我们可以把闭包形象的理解为一道门,屋子里面是内部变量。钥匙是一个引用。
当我们把钥匙给张三这个对象(otherObject1.p1 -& 门),产生了一个引用
当我们再配一把钥匙给李四这个对象(otherObject2.p2 -& 门)产生了另外一个引用
GC在回收的时候会判断一个闭包还有没有人拿着钥匙,要是没有引用或者是内部循环引用(李四在屋子里),就会释放闭包内变量所在的空间,回收垃圾
我斗胆的说一句:严格意义上讲,闭包不是真正产生内存泄漏的原因!各位有意见可以评论里指出,现在举个最简单的例子:
function bindEvent()
var obj = document.createElement("XXX");
obj.onclick = function(){
bindEvent();
这人把钥匙(引用)给了一个外部不可控的dom对象,怎么能怪人家闭包的错误呢!
再看下面代码:
var otherJsObj = {};
function bind()
otherJsObj.func1 = function(){
我把钥匙给了otherJsObj。然后叮嘱它:“你不用的时候就把你的func1置空或者赋值成别的对象,解除我的引用,我好回收垃圾”。这样可控,因为咱们都是自己人(js对象),有访问权限[呵呵]
楼主会产生这样的困惑,是因为闭包确实是在保持对别的对象的引用。也会产生较大的内存占用。但这是可控制的,不是闭包的错。
@李维 把道理讲述的很清楚了,我觉得说得很棒,也很认同他对必包的看法。我想补充一些实战性的建议。
首先,你要清楚一件事情:绝大部分的内存泄露都不是有 Javascript 自身引起的,Javascript 的 GC 已经做的相当不错了(或者说浏览器引擎的 GC,比如 webkit),大多数内存泄露总是源自于和外部 API 的交互,特别是 DOM,以及一些 HTML(5)提供的外部 API。我们都知道传统意义上的 Javascript 其实包含了 ECMAScript+BOM+DOM+Maybe Others,而经典的工作场景又总是在混合环境下(浏览器),所以一出现内存泄露就把屎盆子往 ECMAScript 身上扣是不道德的,而且一出现内存泄露就去找代码纯粹属于 ECMAScript 部分的问题也是盲目的(当然不是没有丝毫可能性)。
如果你确实觉得没底,那么本着务实的精神,首先看一下自己是否遵从了下列原则(欢迎补充):
由于我们的 JS 代码大体上都是依靠事件来驱动的,并且事件基本上都绑定在 DOM 对象上,因此要尽可能的利用 Delegation,把相关联的事件绑定在同一个 DOM 对象上,要记得善加利用事件的冒泡和传播特性。因为我们使用事件不光是为了绑定和监听,更重要的是使用大量的回调函数。如果这些事件乱绑一气,又不会在合适的时刻对它们解绑或者把宿主销毁,那么这些回调函数的引用就会一直被保持着等待触发执行。如果这些函数之中还有必包引用着大量的没有被销毁处理的 DOM 对象……你就可想而知了。
可复用的对象(特别是 DOM 对象)应及早将其引用赋给变量,并保持使用该变量来进行进一步的操作,包括向下的遍历查找,插入、复制新对象等等,这样一来 GC 回收会变得更加高效。如果你能很好的设计应用的流程和交互,那么基本上这些对象都能在恰当的时刻被主动销毁,比如说附个 null 给它的引用,一旦对象没有了任何引用,GC 会很快将其加入销毁队列。哪怕之后还要重新创建它,多数时候也是有益的,你可以通过观测性能指标(后面讲)或者分析用户的行为来找到一个合理的平衡。
为什么 jQuery 的教程总是说:请把 jQuey 对象尽早的使用变量保存起来,而不是反复的使用 $(sameSelector)?道理就在此。
基于对开始的第一条原则,保存一个可复用的上层 DOM 对象,可以使得其 DOM 树自节点需要的事件监听都绑定在父节点身上。
触发事件的时候别一股脑的都往默认事件类型身上丢,jQuery 提供了很好的事件命名空间机制,要善于利用。比如说 $(selector).on('click.my_event', fn),要比$(selector).on('click', fn)漂亮得多。Why?因为你可以随时$(selector).off('click.my_event'),而click你就不敢随便解绑了,因为鬼知道还有谁在监听它?
同样的道理,使用命名函数也要比大量使用匿名函数要合理得多,还是上面那个例子,即使你非要用click,那也不要用匿名函数,否则解绑的时候你怎么去指明要解绑的回调是哪个?
IIFE 是安全的,它永远不会引起内存泄漏,因为没人引用它。
为什么开发者那么偏爱 Google Chrome?因为在开发者工具里,Chrome 的 Timeline(里面有 Memory Analyzer)和 Profiles 工具是做的最棒的!学会使用它,你可以定位和分析一切造成内存泄露的所在及原因,成功的使用它几次,你就会变成内存管理专家的。
这里有一篇非常棒的入门介绍,作者是 Google 的小神(真大神都不带露面的):
这里有 Gmail 团队写的探查和解决内存的演讲稿(可能要翻墙):
实践出真知,学好这些工具,多做实验,有些理论性的东西靠想或是靠别人说都是徒劳,自己发现之后一下就醒悟了。
补充:抱歉,写完了回头一看,题主好像主要关心的是 Node.js,而我之前多数都是和浏览器有关的,所以再补充一点。
其实 Node.js 用的是 V8 引擎嘛,和 chrome 一样的,虽然不是太了解底层有没有对其做出什么改变,但是 GC 相比只会更好不会更差吧?这样一来,问题反而更小了不是么。
有一个比较经典的范例我上面简单提到过,就是强制 GC 去回收你指定的对象,基本上就是这样:
var someFn = function (foo, cb) {
var veryBigObject = new veryBigObject();
doSomeWith(foo).on('event', function (e) {
// 做该做的事
cb(veryBigObject); // 在某处用了大对象,也不一定非得是回调传参,举例而已
// 做该做的事
veryBigObject = // V8! 请你回收它,谢谢!
就是这样了。或许你的应用场景不尽如此,但是原则是一样的:只要你没谱,就在恰当的时候null了它。(或者先观察,找出可能存在泄露的地方再这么做)
再附上 Mozilla 上看到的一篇 Nodejs 处理内存泄露的文章(可能有点过时):
另外还有一篇非常经典的分析典型案例的文章(题主是不是看的这个?):
看过一篇文章,提到javascript内存泄露的成因。
由于引用计数的使用,实际上应该不会出现真正的内存泄露,出现“从代码上分析允许访问”但“程序实际上永远不会访问”的这种“泄露”是有可能的。
但是,写这篇文章的时候(2008年),所有浏览器的内存中,引用计数都是分开进行的:javascript引擎和dom。两部分内存空间中的相互引用有可能导致内存泄露。
特别是使用iframe的时候泄露非常明显,由于js和dom都不能gc,所以内存占用增长非常快。
建议读javascript权威指南
添加你想要问的问题
PHP开发框架
开发工具/编程工具
服务器环境
ThinkSAAS商业授权:
ThinkSAAS为用户提供有偿个性定制开发服务
ThinkSAAS将为商业授权用户提供二次开发指导和技术支持
让ThinkSAAS更好,把建议拿来。
开发客服微信  如果闭包的作用域链中保存着一个HTML元素,那么就意味着该元素将无法被销毁。
1 function assignHandler(){
2   var element = document.getElementById("someElement");
3   element.onclick = function(){
4     alert(element.id);
  而这个闭包则又创建另一个循环引用。由于匿名函数保存了一个对 assignHandler()的活动对象的引用,因此就会导致无法减少 element 的引用数。只要匿名函数存在,element 的引用数至少也是1,因此它所占用的内存就永远不会被回收。
不过稍微改进下就可以了
1 function assignHandler(){
2   var element = document.getElementById("someElement");
3   var id = element.
4   element.onclick = function(){
5     alert(id);
7   element = null;
  在上面的代码中,通过把element.id 的一个副本保存在一个变量中,并且在闭包中引用该变量消除领料循环引用。但仅仅做到这一步,还是不能解决内存泄漏的问题。
必须要记住:闭包会引用包含函数的整个活动对象,而其中包含着element。即使闭包不直接引用element 包含函数的活动对象中也任然会保存一个引用。因此,把element变量设置为null。这样就接触对DOM对象的引用,顺利地减少其引用数,确保正常回收其占用的内存。
阅读(...) 评论()function fors(){
obj_a = obj_b;
obj_b.attr = obj_a;
function fors(){
obj_b = {};
obj_b.attr = obj_b;
上面是两个个很显示的循环引用,IE中产生了内存泄露,由于IE的内存回收机制,导至会长期占用内存而不能释放。
但闭包的内存泄露,有些隐蔽。因为闭包的循环引用,是间接的。
function iememery(){
var js_obj = document.createElement("div");
js_obj.oncontextmenu = function(){ return false;}
}&body onload="iememery()"&
从表面上看,没有任何循环引用。但上面是一个闭包,根据闭包的特性,内部函数有权访问外部函数的变量对象。所以当iememery()执行之后:
js_obj是一个DOM元素的引用,DOM元素它长期在网页当中,不会消失,而这个DOM元素的一属性oncontextmenu,又是内部的函数引用(闭包),而这个匿名函数又和js_obj之间有隐藏的关联(作用域链)
所以形成了一个,循环引用.即:
js_obj.oncontextmenu 间接引用到 js_obj 也就是说,这个对象的一个属性,又间接的引用了自己。
只要有循环引用,就会在IE下产生内存泄露。打开你的windows任务管理器,在IE中不停刷新含有这个代码的html页面,看看Iexploer进程的内存占用情况,一直上升,且不会自动回收(降低);
解决办法:
function iememery(){
var js_obj = document.createElement("div");
js_obj.oncontextmenu = function(){ return false;};
      js_obj.oncontextmenu = null;//加上这句,断开引用 }
当IE中发生js对象与dom对象直接的循环引用,并且之后没有引用指向他们,如果是IE 6, 内存泄漏,直到关闭IE进程为止如果是IE 7,内存泄漏, 直到离开当前页面为止如果是IE 8, GC回收器回收他们的内存,无论当前是不是compatibility模式。之前的IE js引擎里的GC回收器只能处理js对象,不能处理DOM对象。由于IE9之前的版本对JScript对象和COM对象使用不同的垃圾收集例程,因此闭包在IE的这些版本中会导致如上问题。这个匿名函数作为element元素事件处理程序,形成闭包的状态就会保存对父层函数内活动对象的引用,只要匿名函数存在,element的引用数至少也是1,因此它所占用的内存就永远不会被回收。注意,闭包会引用包含函数的整个活动对象,即使闭包不直接引用element,包含函数的活动对象中也仍然会保存一个引用。因此,有必要把使用完的element变量设置为null,解除对DOM对象的引用,确保正常回收其占用的内存。
阅读(...) 评论()}

我要回帖

更多关于 广西女孩子很好泡 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信