浅谈 javascript 中的垃圾收集机制

868 阅读4分钟
原文链接: ykloveyxk.github.io

因为已经研二了,眼看就要找工作了,心中很忐忑。好怕面试官当众吊打我,真的好怕好怕……所以想着开个博客,整理分享一些js的基础知识,充实自己的同时,也能分享给需要的人。


今天想要分享的小知识,是关于javascript的垃圾收集机制(GC(Garbage Collection) )。

自动垃圾收集机制

不同于C/C++需要编程人员手动跟踪内存的使用情况,javascript拥有一套自动垃圾收集机制,也就是说,在我们的开发过程中,并不用去考虑内存的分配和回收问题,这些问题都由执行环境负责,它自动管理代码执行过程中内存的分配与回收,让编码人员能将更多精力放在业务功能实现。而手动跟踪内存自身也有其弊端,例如很繁琐,变量数目很多时人工操作很容易遗漏,造成资源浪费等。

自动垃圾收集机制,它的原理其实很简单:
确定变量中有哪些还在使用,哪些已经不再使用,然后垃圾收集器会按照固定的时间间隔去周期性的释放已经不再继续使用的变量所占的内存。

但是怎么界定变量有没有被使用,就需要考虑它的生命周期。

变量的生命周期

在不考虑卸载页面,注销系统的基础上,全局变量是一直都会存在的,因为系统不明确什么时候还会用到它,所以全局变量通常不会被垃圾收集机制所回收。js面试中的另一个特色问题闭包之所以能够在函数外部读取函数内部局部变量,保证该局部变量不被垃圾收集器回收,就是因为定义了一个外部引用,使得整个函数与全局变量连接在一起。(说的比较笼统,之后在闭包部分详述)

而局部变量只在函数执行的过程中存在,在这个过程中,会为局部变量分配内存空间,用来储存它的值。当函数结束时,局部变量就可以界定为不再继续使用,于是就会释放它的内存。但这只是很简单的一种情况,随着实现的不同,垃圾收集器对“有用”,“无用”的标示策略也不同,总体来说,有以下两种:

  1. 标记清除
  2. 引用计数

标记清除法

标记清除是javascript中最常用的垃圾收集机制。简单的来说就是:变量进入环境(即执行上下文,例如全局环境、函数环境),标记该变量进入环境;出环境则标记变量离开环境。

然后垃圾收集器会保留环境中的变量和被环境中变量引用的变量(其实就是闭包),定时循环释放除此之外的离开环境的变量的内存。大多现代浏览器都采取这个策略进行垃圾收集,它们之间的区别在于垃圾收集器的回收间隔时间。

引用计数法

要明确引用计数法,首先需要了解什么是引用?

此处的引用是指一个对象对另一个对象的访问权限,可以访问,则表示这个对象引用另一个对象。

引用计数法并不常用,出现在IE早期,它的原理也很简单:跟踪每个值被引用的次数,当声明变量并赋给他一个引用类型的值时,则该值引用次数为1,将其赋给另一个变量时,引用次数就变为2,以此类推;相反,如果引用这个值的那个变量引用了别的值,那么这个值的引用次数就减1,当然新引用的那个值的次数加1。最后,当次数变为0时,则默认改值已不再被需要,就会被回收器回收。

但是该方法有一个巨大缺陷,就是它存在循环引用:

function referenceCount() {
	var variable_a = {};
	var variable_b = {};
	variable_a.prop = variable_b;
	variable_b.prop = variable_a;
}
referenceCount();

此时variable_a, variable_b通过各自属性相互引用,但是函数已经执行完毕,本来应该将这两个对象所占内存释放,但在引用计数法中它们不会被回收。


以上,就是关于javascript中垃圾收集机制的一些简单介绍。虽然它可能对我们实际编程意义不大,但是对我们了解javascript这门语言的运行机制,还是很有帮助的。因为我也是前端届的小学生,如有问题,希望大家严厉指出,谢谢。