Garbage Collection,即GC,中文称为垃圾回收。什么是垃圾回收呢?顾名思义,就是回收不需要的东西。正如你会把无用的东西扔到垃圾桶中,在程序中GC的作用就是回收不再使用的内存空间。对于GC,一般而言只有两件事需要做,找到内存空间里面的垃圾,接着回收垃圾,让程序员能够再次利用这部分空间。
诸如C这样的高级语言,提供了malloc()或者free()这样的API要求程序员显示的分配或者释放内存,这种手动的方式相当麻烦,而且容易出错:要是你不小心释放了正在使用中的内存,你可能就会遇到由此引发的恶性BUG。
而JavaScript是一门宽容的语言。
JavaScript的实现已经好了搭载GC。通过GC,程序员把内存管理交给计算机,摆脱了手动释放内存的痛苦。即便拥有了GC,能让开发者告别复杂的内存管理工作,但如果因此而完全忽略内存管理的存在,你一样会因为GC停顿或者不断往上涨的内存泄漏而痛苦。当然,很多时候并不是因为你发现了它们而痛苦,而是因为糟糕的体验和客户的抱怨。所以,哪怕对于JavaScript这样的语言,也需要你了解一些GC对于这门语言的影响。
首先你要明白一点,你不能去控制GC去强制回收某块内存,GC是一个运行时程序。
目前来说,GC的算法归根结底可以分为三类:GC标记-清除法,引用计数法,GC复制算法。目前实现的各种各样的GC算法其实都是这三种算法的变形组合,不过本文不会讨论这些算法本身。
GC的概念和实现
首先,我们先从引擎的角度来了解下,GC在引擎内是如何工作的,你创建的对象是如何被GC认定为可以回收的,以及你在编写代码时应该注意的。
通过下面内容,我们会弄明白这几个问题:
- 存于内存中的值是什么样子的?
- 这些值如何被组织的?
- 什么是垃圾?
- 什么是泄露?
GC的组成
在GC的世界中,内存分配和回收的最小单元是对象。对象在这里不再指“具有属性和行为的事物”,而是表示“通过应用程序利用的数据的集合”。在这里的数据可以是你声明的变量,创建的对象或者是进入上下文创建的环境记录项等。在本文中,会通过“GC对象”和“JavaScript对象”来区分GC中对象和JavaScript语言定义的对象。
一般来说,GC对象由头(head)和域(filed)组成。
头是用来保存对象本身的信息,比如:对象的大小,对象的类型等,GC需要这类信息来判断内存中储存的GC对象的边界。
域中存的是对象使用者实际需要访问的内容,也就是应用程序的数据,域可以包含多个部分的内容,每个部分会储存字面量值本身或者指向其他对象的指针。
需要注意的是,GC是管理堆中已分配对象的机制。对于堆栈中的内容,有栈帧的机制来管理。
JavaScript值在内存中的组织
下面以这张图为例,我们来看下JavaScript值在内存中的组织。