JavaScript具有自动垃圾收集机制,执行环境会负责管理代码执行过程中使用的内存。而在C和C++之类的语言中,开发人员的一项基本任务就是手工跟踪内存的使用情况,这是造成许多问题的一个根源。这种垃圾收集机制的原理其实很简单:找出那些不再继续使用的变量,然后释放其占用的内存。为此,垃圾收集器会按照固定的时间间隔(或代码执行中预定的收集时间)周期性地执行这一操作。
垃圾收集器必须跟踪哪个变量有用哪个变量没用,对于不再有用的变量打上标记,以备将来收回其占有的内存。标识无用变量的策略可能会因实现而异,但具体到浏览器中的实现,则通常有两个策略。
-
标记清除(最常用的垃圾收集方式)
当变量进入环境(例如,在函数中声明一个变量)时,就将这个变量标记为“进入环境”。从逻辑上讲,永远不能释放进入环境的变量所占用的内存,因为只要执行流进入相应的环境,就可能会用到它们。而当变量离开环境时,则将其标记为“离开环境”。
垃圾收集器在运行的时候会给存储在内存中的所有变量都加上标记(当然,可以使用任何标记方式)。然后,它会去掉环境中的变量以及被环境中变量引用的变量标记。而在此之后再被加上标记的变量将被视为准备删除的变量,原因是环境中的变量已经无法访问到这些变量了。垃圾收集器销毁那些带着标记的值并收回它们所占用的内存空间。
-
引用计数
引用计数的含义是跟踪记录每个值被引用的次数。当声明一个变量并将一个引用类型的值赋给该变量时,则这个值的引用次数就是1。如果同一个值又被赋给另一个变量,则该值的引用次数加1。相反,如果包含对这个值引用的变量又取得了另外一个值,则这个值的应用次数减1。当这个值的引用次数变成0时,则说明没有办法再访问这个值了,因而就可以将其占用的内存空间收回来。当垃圾收集器下次再运行时,它就会释放那些引用次数为0的值所占用的内存。
存在循环引用的问题
function problem(){ var objectA = new Object(); var objectB = new Object(); objectA.someOtherObject = objectB; objectB.someOtherObject = objectA; } // 引用次数永远不会是0
-
性能问题
垃圾收集器是周期性运行的,如果为变量分配的内存数量很可观,那么回收工作量也是相当大的。需要确定垃圾收集的时间间隔是一个非常重要的问题。
IE的垃圾收集器是根据内存分配量运行的,具体一点说就是256个变量、4096个对象(或数组)字面量和数组元素(slot)或者64KB的字符串。达到上述任何一个临界值,垃圾收集器就会运行。如果一个脚本中包含那么多量,那么该脚本很可能会在其生命周期中一直保有那么多的变量。这样一来,垃圾收集器就得频繁运行。
随着IE7的发布,js引擎的垃圾收集例程改变了工作方式:触发垃圾收集的变量分配、字面量和数组元素的临界值被调整为动态修改。如果垃圾收集例程回收的内存分配量低于15%,则变量、字面量和数组元素的临界值就会加倍。如果例程回收了85%的内存分配量,则将各种临界值重置回默认值。
-
管理内存
JavaScript在进行内存管理及垃圾收集时面临的问题还是有点与众不同。因为分配给Web浏览器的可用内存数量通常要比分配给桌面应用程序的少。出于安全考虑,防止运行JavaScript的网页耗尽全部系统内存而导致系统崩溃。内存限制问题不仅会影响给变量分配内存,同时还会影响调用栈以及在一个线程中能够同时执行的语句数量。
解除引用:一旦数据不再有用,最好通过将其值设置为null来释放引用。
这一做法适用于大多数全局变量和全局对象的属性。局部变量会在它们离开执行环境时自动被解除引用。不过,解除一个值的引用并不意味着自动回收该值所占用的内存。解除引用的真正作用是让值脱离执行环境,以便垃圾收集器下次运行时将其收回。