JavaScript闭包如何被垃圾回收

2020/10/24 17:21 · javascript ·  · 0评论

我已记录以下Chrome错误,这导致我的代码中发生了许多严重且不明显的内存泄漏:

(这些结果使用Chrome Dev Tools的内存配置文件,该内存配置文件运行GC,然后对未垃圾收集的所有内容进行堆快照。)

在下面的代码中,someClass实例被垃圾回收(良好):

var someClass = function() {};

function f() {
  var some = new someClass();
  return function() {};
}

window.f_ = f();

但是在这种情况下,它不会被垃圾回收(不好):

var someClass = function() {};

function f() {
  var some = new someClass();
  function unreachable() { some; }
  return function() {};
}

window.f_ = f();

以及相应的截图:

Chromebug的屏幕截图

function() {}如果在同一上下文中任何其他闭包都引用了该对象,那么闭包(在这种情况下为似乎会使所有对象保持“活动状态”,无论该闭包本身是否可以访问。

我的问题是有关其他浏览器(IE 9+和Firefox)中关闭的垃圾回收。我对webkit的工具(例如JavaScript堆分析器)非常熟悉,但是我对其他浏览器的工具知之甚少,因此我无法对其进行测试。

IE9 +和Firefox将在这三种情况中的哪一种情况下垃圾收集 someClass 实例?

据我所知,这不是错误,而是预期的行为。

来自Mozilla的“内存管理”页面:“从2012年开始,所有现代浏览器都附带标记清除垃圾收集器。” “限制:必须使对象明确不可访问

在您的示例中,它some仍然可以在关闭中失败我尝试了两种方法使它无法访问,并且两种方法都能正常工作。some=null可以在不再需要时进行设置,或者进行设置后window.f_ = null;它就会消失。

更新资料

我已经在Windows的Chrome 30,FF25,Opera 12和IE10中进行了尝试。

标准未对垃圾回收作任何说明,但提供了一些应该发生的线索。

  • 第13节,函数定义,第4步:“让闭包成为创建13.2中指定的新Function对象的结果”
  • 第13.2节“由范围指定的词汇环境”(范围=闭包)
  • 第10.2节词汇环境:

“(内部)词法环境的外部引用是对在逻辑上围绕内部词法环境的词法环境的引用。

外部词法环境当然可以具有自己的外部词法环境。词法环境可以用作多个内部词法环境的外部环境。例如,如果一个函数声明包含两个嵌套的函数声明,则每个嵌套函数的词法环境将以周围函数当前执行的词法环境作为其外部词法环境。”

因此,一个函数将有权访问父级的环境。

因此,some应该在关闭返回函数时可用。

那为什么不总是可用呢?

在某些情况下,Chrome和FF似乎足够聪明,可以消除该变量,但是在Opera和IE中,some变量都可以在闭包中使用(注意:查看此设置的断点return null并检查调试器)。

可以改进GC以检测功能中是否some使用了GC ,但这将很复杂。

一个不好的例子:

var someClass = function() {};

function f() {
  var some = new someClass();
  return function(code) {
    console.log(eval(code));
  };
}

window.f_ = f();
window.f_('some');

在上面的示例中,GC无法知道是否使用了该变量(已测试代码并且可在Chrome30,FF25,Opera 12和IE10中运行)。

如果通过向分配另一个值来破坏对对象的引用,则会释放内存window.f_

我认为这不是错误。

我在IE9 +和Firefox中对此进行了测试。

function f() {
  var some = [];
  while(some.length < 1e6) {
    some.push(some.length);
  }
  function g() { some; } //removing this fixes a massive memory leak
  return function() {};   //or removing this
}

var a = [];
var interval = setInterval(function() {
  var len = a.push(f());
  if(len >= 500) {
    clearInterval(interval);
  }
}, 10);

现场直播在这里

我希望以function() {}最少的内存完成500个数组

不幸的是,事实并非如此。每个空函数都保留着一百万个数组(永远无法访问,但不是GC运算的)。

Chrome最终停止运行并死了,Firefox使用了将近4GB的内存后完成了整个工作,IE逐渐变慢,直到显示“ Out of memory”。

删除任一注释行即可修复所有问题。

似乎所有这三种浏览器(Chrome,Firefox和IE)都按上下文而不是按关闭保存环境记录。鲍里斯(Boris)提出了做出这一决定的原因是性能,这似乎是可能的,尽管我不确定根据上述实验该如何称呼性能。

如果需要闭包引用some(当然,我没有在这里使用它,但是想像我在使用它),如果不是

function g() { some; }

我用

var g = (function(some) { return function() { some; }; )(some);

通过将闭包移动到与其他函数不同的上下文中,它将解决内存问题。

这将使我的生活更加乏味。

PS出于好奇,我在Java中尝试了此操作(利用其在函数内部定义类的能力)。GC可以像我最初希望的那样运行。

启发式方法各不相同,但是实现此类操作的一种常见方法是为f()您的案例中的每个调用创建一个环境记录,并且仅将f实际上已关闭(通过某种关闭)关闭的本地存储在该环境记录中。然后,在调用中创建的任何闭包都会f使环境记录保持活动状态。我相信至少这是Firefox实现关闭的方式。

这样的好处是可以快速访问封闭变量,并且实现简单。它具有观察到的效果的缺点,即在某些变量上关闭的短期关闭会导致长期关闭而使它保持活动状态。

一个人可以尝试为不同的闭包创建多个环境记录,具体取决于它们实际关闭的内容,但是这可能很快变得非常复杂,并可能导致自身的性能和内存问题。

  1. 保持函数调用之间的状态假设您有函数add(),并且希望它在几次调用中将传递给它的所有值相加并返回总和。

像add(5); //返回5

加(20); //返回25(5 + 20)

add(3); //返回28(25 + 3)

您可以通过两种方式首先执行此操作:正常定义全局变量
当然,您可以使用全局变量来保存总数。
但是请记住,如果您(不使用)全局变量,那么这个家伙会吞噬您的生命。

现在使用闭包而不定义全局变量的最新方法

(function(){

  var addFn = function addFn(){

    var total = 0;
    return function(val){
      total += val;
      return total;
    }

  };

  var add = addFn();

  console.log(add(5));
  console.log(add(20));
  console.log(add(3));
  
}());
function Country(){
    console.log("makesure country call");	
   return function State(){
   
    var totalstate = 0;	
	
	if(totalstate==0){	
	
	console.log("makesure statecall");	
	return function(val){
      totalstate += val;	 
      console.log("hello:"+totalstate);
	   return totalstate;
    }	
	}else{
	 console.log("hey:"+totalstate);
	}
	 
  };  
};

var CA=Country();
 
 var ST=CA();
 ST(5); //we have add 5 state
 ST(6); //after few year we requare  have add new 6 state so total now 11
 ST(4);  // 15
 
 var CB=Country();
 var STB=CB();
 STB(5); //5
 STB(8); //13
 STB(3);  //16

 var CX=Country;
 var d=Country();
 console.log(CX);  //store as copy of country in CA
 console.log(d);  //store as return in country function in d
(function(){

   function addFn(){

    var total = 0;
	
	if(total==0){	
	return function(val){
      total += val;	 
      console.log("hello:"+total);
	   return total+9;
    }	
	}else{
	 console.log("hey:"+total);
	}
	 
  };

   var add = addFn();
   console.log(add);  
   

    var r= add(5);  //5
	console.log("r:"+r); //14 
	var r= add(20);  //25
	console.log("r:"+r); //34
	var r= add(10);  //35
	console.log("r:"+r);  //44
	
	
var addB = addFn();
	 var r= addB(6);  //6
	 var r= addB(4);  //10
	  var r= addB(19);  //29
    
  
}());
本文地址:http://javascript.askforanswer.com/javascriptbibaoruhebeilajihuishou.html
文章标签: ,   ,   ,   ,  
版权声明:本文为原创文章,版权归 javascript 所有,欢迎分享本文,转载请保留出处!

文件下载

老薛主机终身7折优惠码boke112

上一篇:
下一篇:

评论已关闭!