JSCore
javascript 是一种解析型脚本语言,代码不进行预编译,代码在运行的过程中逐行进行解析。因此,需要一个东西对它进行解析,这就是JSCore(统称)。
能解析js的软件有很多,其中v8性能显著,而大多数的js代码执行,基本都遵循以下流程:
词法分析
把js代码分解成 Token 序列的过程, 也可以叫做分词
这一过程由 Lexer完成,有的编译器或者解析器把分词叫做 Scanner
例如下面的 表达式
sum = 3 + 2 |
将其标记之后,得到下表的内容:
词法分析不会关注每个token之间的关联,是否匹配,
仅仅是把他们区分开来,等待语法分析来把这些 Token 串起来。
语法分析 Parser
就像人与人交流一样,语言都是有语法规则的,双方都懂语法,才能听的懂。
因此,Parser会对Tokens序列进行语法分析,并生成对应的一颗抽象语法树(AST)
生成字节码 ByteCodeGenerator
以 v8 为例讲述
在 v8中,将 AST 转换为字节码这一过程叫Ignition 阶段。
V8引擎的诞生带着使命而来,就是要在速度和内存回收上进行革命的。JavaScriptCore(Safari的JSCore)的架构是采用生成字节码的方式,然后执行字节码。Google觉得JavaScriptCore这套架构不行,生成字节码会浪费时间,不如直接生成机器码快。所以V8在前期的架构设计上是非常激进的,采用了直接编译成机器码的方式。
早期的V8有Full-Codegen和Crankshaft两个编译器。V8 首先用 Full-Codegen把所有的代码都编译一次,生成对应的机器码。JS在执行的过程中,V8内置的Profiler筛选出热点函数并且记录参数的反馈类型,然后交给 Crankshaft 来进行优化。所以Full-Codegen本质上是生成的是未优化的机器码,而Crankshaft生成的是优化过的机器码。
随着版本的引进,网页的复杂化,V8也渐渐的暴露出了自己架构上的缺陷:
Full-Codegen编译直接生成机器码,导致内存占用大
Full-Codegen编译直接生成机器码,导致编译时间长,导致启动速度慢
Crankshaft 无法优化try,catch和finally等关键字划分的代码块
Crankshaft新加语法支持,需要为此编写适配不同的Cpu架构代码
为了解决上述缺点,V8还是采用JavaScriptCore(Safari的JSCore)的架构,生成字节码。
Ignition是V8的解释器,用于将抽象语法树转换成字节码
生成机器码 (LLint)
还是以v8 为例:
v8 通过 Turbofan将字节码转位机器码
TurboFan 编译器,它是 JIT 优化的编译器,旨在解决Crankshaft的缺点。 Crankshaft只能优化JavaScript语言的子集。例如,它不是设计用于使用结构化异常处理优化JavaScript代码,即由JavaScript的try,catch和finally关键字划分的代码块。很难在Crankshaft中添加对新语言功能的支持,因为这些功能几乎总是需要为九个支持的平台编写特定于体系结构的代码。
同时 V8 引擎是多线程的,TurboFan 的编译线程和生成字节码不会在同一个线程上,这样可以和 Ignition 解释器相互配合着使用,不受另一方的影响。
Ignition的字节码可以直接用TurboFan生成优化的机器代码,而不必像Crankshaft那样从源代码重新编译。Ignition的字节码在V8中提供了更清晰且更不容易出错的基线执行模型,简化了去优化机制,这是V8 自适应优化的关键特性。最后,由于生成字节码比生成Full-codegen的基线编译代码更快,因此激活Ignition通常会改善脚本启动时间,从而改善网页加载。
垃圾回收机制 (GC)
js执行后,是会占用内存空间的,但JS本身并不具备释放空间的能力,因此,JSCore需要有GC机制
至此,js就解析执行完了。