# 编译原理与回收机制

# 编译原理

JavaScript (JS) 是一种解释型语言,它的编译原理与传统编译型语言(如C或C++)有所不同。在传统的编译型语言中,源代码被完全转换为机器码,然后执行。而在 JavaScript 中,这个过程更加复杂和动态。以下是 JavaScript 编译的基本步骤:

1、词法分析/分词(Lexical Analysis/Lexing):

在这个阶段,源代码被分割成一系列有意义的符号(tokens)。例如,关键字、变量名、运算符等。

2、语法分析/解析(Parsing):

解析器将这些 tokens 组合成一个抽象语法树(AST, Abstract Syntax Tree),这是一个表示程序结构的树状数据结构。

3、代码生成(Code Generation):

从 AST 创建字节码或者直接生成机器码。对于现代 JavaScript 引擎来说,它们通常会生成中间表示(IR, Intermediate Representation)代码。

4、优化(Optimization):

某些引擎可能会对 IR 进行优化,以提高性能。这包括内联函数调用、消除死代码等。

5、编译(Compilation):

最终,经过优化的 IR 被编译成机器码,可以直接由 CPU 执行。

6、执行(Execution):

生成的机器码被执行。同时,某些现代 JavaScript 引擎会在运行时监控代码的行为,并可能进行即时编译(JIT, Just-In-Time Compilation)以进一步优化热路径上的代码。 JavaScript 引擎,比如 Google 的 V8 或 Mozilla 的 SpiderMonkey,在上述过程中加入了许多高级特性,比如:

  • 隐藏类(Hidden Classes):用于加速属性访问。
  • 内联缓存(Inline Caching):加快对象属性的查找速度。
  • 多态内联缓存(Polymorphic Inline Caches):处理多个不同类型的对象访问。
  • 垃圾回收(Garbage Collection):自动管理内存分配和释放。

这些机制使得 JavaScript 引擎能够在保持灵活性的同时提供良好的性能。需要注意的是,具体的实现细节会根据不同的 JavaScript 引擎而有所差异

# 垃圾回收机制

JavaScript 的垃圾回收机制(Garbage Collection, GC)是自动化的,它负责管理内存分配和释放。开发者不需要手动管理内存,这减少了编程时的复杂性和出错的可能性。以下是 JavaScript 中垃圾回收的主要方法和技术:

引用计数垃圾收集

早期版本的浏览器使用引用计数来跟踪对象的生命周期。每个对象都有一个引用计数器,记录有多少个地方在引用这个对象。当一个对象不再被任何地方引用(即它的引用计数为零),它就被认为是可以回收的。

然而,引用计数有一个主要的问题:循环引用。如果两个或更多对象相互引用,即使它们不再被外部代码访问,它们的引用计数也不会降为零,因此无法被垃圾回收器识别为可回收的对象。

标记-清除(Mark-and-Sweep) 现代 JavaScript 引擎大多采用标记-清除算法来处理垃圾回收。其工作原理如下:

  • 1、标记阶段:从一组“根”对象(如全局对象、当前执行的函数等)开始,垃圾回收器会遍历所有可达对象,并对这些对象做上标记。
  • 2、清除阶段:一旦标记完成,垃圾回收器会遍历堆中的所有对象,将未标记的对象视为垃圾并回收它们占用的内存。

这种方法可以有效解决循环引用的问题,因为它只关注那些仍然活跃的对象(即可以从根对象直接或间接访问到的对象)。

增量标记(Incremental Marking)

为了减少长时间暂停应用程序进行垃圾回收所带来的影响,一些引擎实现了增量标记技术。这意味着垃圾回收过程不是一次性完成,而是分多个小步骤完成,这样可以在每次回收一点内存的同时允许程序继续运行。

代际假设与分代收集(Generational Collection)

大多数垃圾回收器都基于一个经验法则:大部分对象很快就会变成垃圾(称为“弱世代假设”)。基于此,垃圾回收器将对象分为不同的“代”:

  • 年轻代(Young Generation):新创建的对象首先会被放置在这里。因为这里的对象更有可能成为垃圾,所以这个区域会更频繁地进行垃圾回收。
  • 老年代(Old Generation):存活了一段时间的对象会被移动到这里。由于这里的对象较稳定,垃圾回收频率较低。

通过这种方式,垃圾回收器能够更加高效地管理内存,同时尽量减少对性能的影响。

对象可达性分析

除了上述技术外,JavaScript 引擎还会利用其他优化策略,例如对象形状(shape)共享、内联缓存等,以提高垃圾回收效率和整体性能。

总之,虽然 JavaScript 的垃圾回收机制是自动化的,但了解其工作原理可以帮助开发者编写更高效的代码,避免不必要的内存泄漏等问题。