从头开始学JavaScript (十)——垃圾收集

原文:从头开始学JavaScript (十)——垃圾收集

一、垃圾收集

1.1javascript垃圾收集机制:

自动垃圾收集,执行环境会负责管理代码执行过程中的使用的内存。而在C和C++之类的语言中,开发人员的一项基本任务就是手动跟踪内存的使用情况,这是造成许多问题的一个根源。在编写javascript程序时候,开发人员不用再关心内存使用的问题,所需内存的分配 以及无用的回收完全实现了自动管理。

1.2垃圾收集原理:

  • 找出那些不再继续使用的变量,然后释放其中占用的内存。
  • 垃圾收集器会按照固定的时间间隔(或代码执行中预设的收集时间),周期性的执行这一操作。

1.3垃圾收集方法:

1.3.1标记清除:当变量进入环境时标记“进入环境”,而当变量离开环境时,这将其 标记为“离开环境”。

1.3.2引用计数:引用计数的含义是跟踪记录每个值被引用的次数。当声明一个变量并将引用类型的值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得另外一个值,则这个值的引用次数减1.当这个值的引用次数变成0时,则说明没有办法访问这个值了,因此就可以将其占用的内存空间回收回来。这样当垃圾收集器下次再运行时,它就会释放那些引用次数为零的值所占用的内存。

例如:

1         function countMethod(){
2             var object1 = new Object(); // 声明变量,计数器由 0 变为 1
3             var object2 = new Object(); // 声明变量,计数器由 0 变为 1
4             object1.method1 = "This is object1";  // object1 计数器 -1,object1 读数变为0
5             object2.method2 = "This is object2";  // object2 计数器 -1,object2 读数变为0
6         }

当函数运行结束后,object1 object2的读数均为 0,在下一个垃圾收集周期时,会被回收并且释放其所占用的内存。

可以把引用计数想象成猴子掰玉米,玉米的使用权是变量(之所以是使用权而不是玉米本身,是因为引用计数是对引用类型值而言的),猴子是计数器。第一个猴子拿到玉米使用权,计数器+1;第二个猴子来和第一个猴子分享玉米的使用权,计数器+2;其中一个猴子发现一个更大的玉米的使用权,放弃这个玉米的使用权,计数器—1,另一个猴子也发现一个更大的玉米的使用权,放弃这个玉米的使用权,计数器就变成0,这个玉米就被回收了~(例子举得不恰当还望见谅,只是帮助理解~)

再看下面一个例子:

1             function countMethod(){
2                   var object1 = new Object(); // 声明变量,计数器由 0 变为 1
3                   var object2 = new Object(); // 声明变量,计数器由 0 变为 1
4                   object1.method1 = object2;  // object1 计数器 -1,object2 计数器 +1
5                   object2.method2 = object1;  // object1 计数器 +1,object2 计数器 -1
6             }

这个例子就相当于两个猴子都分别有一个玉米使用权,但是总觉得对方手里的玉米的使用权所对应的玉米比自己大,所以不停地在交换玉米的使用权。也就是循环引用。(例子举得不恰当还望见谅,只是帮助理解~)

此函数运行退出后,object1 的计数器读数为 1,object2 的计数器度数为 1。所以两个变量都不会被销毁。如果大量的这样的程序存在于函数体内,就会导致大量的内存被浪费而无法回收,从而导致内存的泄露。

上述问题可以通过手动释放 object1 object2 所占用的内存来消除。即

1                  object1.method1 = null;
2                  object2.method2 = null;

二、性能问题

垃圾收集器都是周期性运行的,而且如果为变量分配的内存数量很客观,那么回收工作量也是相当大的。在这种情况下,确定垃圾收集的时间间隔是一个非常重要的问题。说到垃圾收集器多长时间运行一次,不禁让人联想到IE因此声名狼藉的性能问题。IE的垃圾收集器是根据内存分配量运行的,具体一点说就是256个变量、4096个对象(或数组)字面量和数组元素(slot)或者64KB的字符串。达到上述任何一个临界值,垃圾收集器就会运行。这种实现的问题在于,如果一个脚本中包含那么多变量,那么该脚本很可能会在其生命周期中一直保持那么多的变量。而这样一来,垃圾收集器就可能不得不频繁的运行。结果,由此引发的严重性能问题初始IE7重写了其垃圾收集例程。

【上面这段话可以这么理解:垃圾收集器可以看做是勤劳的环卫工人,内存分配量可以看做是垃圾箱的容积,如果一个小区的居民造垃圾的能力是单位时间恒定的,也就是说每个回收周期都能把垃圾箱堆满,那么环卫工人会累趴的。。。】(例子举得不恰当还望见谅,只是帮助理解~)

随着IE7的发布,其javascript引擎的垃圾收集例程改变了工作方式:触发垃圾收集的变量分配、字面量和(或)数组元素的临界值被调整为动态修正。IE7中的各项临界值在初始化时与IE6相等。如果例程回收的内存分配量低于15%,则变量 、字面量和(或)数组元素的临界值就会加倍。如果例程回收了85%的内存分配量,则将各种临界重置会默认值。这一看似简单的调整,极大地提升了IE在运行包含大量javascript的页面时的性能。

事实上,在有的浏览器中可以触发垃圾收集过程,但不建议这样做。在IE中,调用window.CollectGarbage()方法会立即指向垃圾收集,在Opera7及更高版本中,调用widnow.opera.collect()也会启动垃圾收集例程。

三、管理内存

确保占用最少内存可以让页面获得更好的性能,最好通过将其值设置为null来释放其引用——这个做法叫做解除引用(dereferencing)。这一做法是用于大多数全局变量和全局对象的属性。局部变量会在他们执行环境时自动被解除引用,如下面这个例子所示:

1 function createPerson (name) {
2     var localPerson = new Object();
3     localPerson.name = name;
4     return localPerson;
5 };
6 var gllbalPerson = createPerson("Nicholas");
7
8 // 手工解除globalPerson的引用
9 globalPerson = null;

最后一 行代码就是解除引用。

解除一个值的引用并不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其回收。

就相当于你把垃圾从家里拿出来扔到小区的垃圾桶,垃圾只是脱离了你家这个执行环境,但是并未被垃圾场回收,只有等下一个打扫周期,勤劳的环卫工人用垃圾车拉走到垃圾场,才算是将其回收。(例子举得不恰当还望见谅,只是帮助理解~)

时间: 2024-07-31 08:57:25

从头开始学JavaScript (十)——垃圾收集的相关文章

从头开始学JavaScript (十二)——Array类型

原文:从头开始学JavaScript (十二)--Array类型 一.数组的创建 注:ECMAscript数组的每一项都可以保存任何类型的数据 1.1Array构造函数 var colors = new Array(); 如果预先知道数组要保存的项目数量,也可以给构造函数传递该数量: var colors = new Array(20); 也可以向构造函数传递数组中应该包含的项: var colors = new Array("red", "blue", "

从头开始学JavaScript (九)——执行环境和作用域

原文:从头开始学JavaScript (九)--执行环境和作用域 一.执行环境:定义了变量或者函数有权访问的其他数据,决定了它们各自的行为.每个执行环境都有与之关联的变量对象. 变量对象:保存着环境中定义的变量和函数. 作用域链:保证对执行环境有权访问的所有变量和函数的有序访问. 标识符解析:沿着作用域链一级一级地搜索标识符的过程. 通过例子来说明执行环境.变量对象以及作用域链: 1 <script type="text/javascript"> 2 var color =

从头开始学JavaScript (四)——操作符

原文:从头开始学JavaScript (四)--操作符 一.一元操作符 1.自增自减操作符:分为前置型和后置型: 前置型:++a;--a; 后置型:a++;a--; 例: 1 <script type="text/javascript"> 2 var a, b,i= 1,j=1; 3 a=i++; 4 b=++j; 5 alert("a="+a+",i="+i+",b="+b+",j="+j);

从头开始学JavaScript (五)——操作符(二)

原文:从头开始学JavaScript (五)--操作符(二) 一.乘性操作符 1.乘法:*      乘法操作符的一些特殊规则: 如果操作数都是数值,按照常规的乘法计算,如果乘积超过了ECMAscript数值的表示范围,则返回infinity或者-infinity 如果有一个操作数是NaN,那返回结果就是NaN 如果是infinity与0相乘,返回NaN 如果infinity与非0数相乘,返回infinity或者-infinity infinity与infinity相乘,返回infinity 如

从头开始学JavaScript (八)——变量

原文:从头开始学JavaScript (八)--变量 一.变量分类: 基本类型值:null.undefined.number.string.Boolean: 引用类型值:保存在内存中的对象,如:Object / Array / Function / Date / RegExp / Error / Map / Set - 二.属性 二者的定义方式是类似的:创建一个变量并为其赋值. 2.1基本类型值不能添加删除属性 2.2引用类型的属性可以动态添加删除属性 例如: 1 <script type=&quo

从头开始学JavaScript (六)——语句

原文:从头开始学JavaScript (六)--语句 一.条件分支语句:if 基本格式: if (<表达式1>){    <语句组1>}else if (<表达式2>){    <语句组2>}else{    <语句组3>}  执行流程: 二.循环语句 2.1前测试循环语句:在循环体内的代码被执行之前就对出口条件求值. 2.1.1while语句 基本格式: do {    <语句组>} while (<表达式>)  执行流

从头开始学JavaScript (十一)——Object类型

原文:从头开始学JavaScript (十一)--Object类型 一.object类型 一个object就是一系列属性的集合,一个属性包含一个名字(属性名)和一个值(属性值). object对于在应用程序中存储和传输数据而言,是非常理想的选择 二.创建object 创建object实例有两种方法: 使用new 操作符后跟object构造函数 使用对象初始化器,也就是对象字面量表示法 2.1使用new 操作符后跟object构造函数创建object实例: 1 var person = new O

Ruby on rails开发从头来(五十六)- ActiveRecord基础(一对多关联关系)

一对多关联可以使我们表示一组对象,例如,一个order可以包含有任意多个line item,在数据库中,所有的line item记录都通过外键关联到特定的order. 在Active Record中,通过在父对象中的has_many来定义到子对象的关联,在子对象中使用belongs_to来指定父对象.我们已经在上一篇中了解了belongs_to声明,实际上,在一对多的情况下,和一对一是相同的,所以我们来了解has_many声明. 开发从头来(五十六)- ActiveRecord基础(一对多关联关

Ruby on rails开发从头来(五十五)- ActiveRecord基础(一对一关联关系)

一对一关联,或者更正确的说是一对零或一对一关联,是通过外键引用到另外一张表中的至多一条记录实现的,下图描述了orders表和invoices表的关系: 开发从头来(五十五)- ActiveRecord基础(一对一关联关系)-rails activerecord"> 在Active Record中,要表示这样的关系需要在Order类中添加has_one:Invoice声明,并且同时在Invoice类中添加声明belongs_to:order,事实上,我们可以把这种关联关系看作是相互的,我们可