JavaScript引擎编译缓存区管理策略
在直接影响JavaScript引擎执行性能方面,即时编译执行和解释执行之间一个最重要的区别就是即时编译执行中JavaScript引擎设置了专门的编译缓存区来存储即时编译生成的对应平台的机器码,JavaScript引擎中的编译缓存区与电脑硬件上面的缓存不同,他是JavaScript引擎在内存中开辟的一块用于为即时编译服务的存储区域,当JavaScript引擎在执行过程总需要再次执行同一段JavaScript代码时,则直接从编译缓存区中查找,将响应的对应平台的机器码拷贝到执行缓冲池中进行执行,这样减少了JavaScript代码中的重新编译频率,提高了代码重用率,减少了编译开销。
编译缓存区现有管理策略
JavaScript引擎在对JavaScript代码进行即时编译时,主要工作包括了对JavaScript的编译和对生成的对应平台的机器码的链接,这些都是在编译缓存区中进行的,JavaScript引擎在即时编译时需要将编译生成的对应平台的机器码按照地址顺序连续的存储到编译缓存中,以确保生成的对应平台的机器码在执行时在物理地址上是连续的
由于JavaScript引擎对JavaScript代码即时编译生成的机器码无法事先预测大小,因此无法准确的确定编译缓存区的大小,如果编译缓存区的大小在初始化时设置的过大虽然会保证生成的机器码能够完全存入编译器缓存中,但是对于嵌入式设备有限的内存空间则会造成存储空间的资源紧张。而如果编译缓存区的大小设置的过小又会无法保证编译生成的机器码能够完整的存入缓存区中,因此JavaScript引擎现有的编译缓存区的管理策略是:首先,JavaScript引擎将编译缓存区的初始大小设置为一个M的阈值,随着JavaScript引擎对JavaScript代码即时编译生成的机器码存入编译缓存中,编译缓存区的空间减少,当机器码在存入编译缓存区之间空闲缓冲区的空间小于待存入的机器码的大小时,JavaScript引擎会重新开辟一个1.5倍大小与一个M的编译缓冲区,将新阈值设置为原先阈值的1.5倍。接着javas引擎会把原先编译缓存区中存储的机器码完整的拷贝到新开辟的编译缓存区中,同时释放原有的编译缓存区空间,随着即时编译过程的进行,如果新开辟的存储空间无法再次存入生成的机器码的时候,则需要再次使用该方式开辟新的编译缓存区空间,如下图

上述的管理策略虽然能够解决编译缓存区初始化设定大小和编译缓存区存储空间不够导致无法存入生成的机器码的问题,但是也存在一下问题:该管理策略中含有JavaScript引擎对新的存储空间的申请和开辟工作,大量机器码的拷贝工作以及原有存储空间的释放工作,这些执行操作会造成大量执行开销,导致嵌入式设备内存紧张,如果降低阈值的大小,开辟空间、拷贝数据和释放原空间的次数将会增加,浏览器在加载网页时的CPU占用率也将大大的增加,甚至出现“卡死”现象,编译缓存区是JavaScript引擎在即时编译模式下生成机器码和执行机器码的重要组成部分,该管理策略会严重降低JavaScript引擎在即时编译模式的执行效率,成为JavaScript引擎执行性能的瓶颈。因此需要对现有的JavaScript引擎的编译缓存器的管理策略进行改进,提高JavaScript引擎的执行效率
编译缓存区的管理策略改进和实现
为了解决上述问题,在改进的编译缓存区中,JavaScript引擎对编译缓存区空间离散的管理方式,将编译缓存区初始化大小设置为一个基本单元缓存块的大小,其管理模式为:
JavaScript引擎会在JavaScript引擎启动时预设编译缓存区的大小为一个固定值,而当JavaScript引擎将编译生成的某段机器码存入到编译缓存区时,JavaScript引擎仅仅只分配一块基本单元缓存块对机器码进行存储,并通过链表进行连接,如果存入空间不够JavaScript引擎再次分配一块基本单元缓存块用于存储某段机器码剩余的部分,这种存储空间分配方式即消除了嵌入式设备的内存空间的压力,也保证了存储空间的动态增长能够满足即时编译生成的机器码的存储,同时也提高了JavaScript引擎对编译编译缓冲区的管理效率
编译缓存区数据结构改进与设计
编译缓存区数据结构分析
JavaScript引擎现有的编译缓存区的数据结构是数组结构,查找效率较低,机器码的存储开销也比较大,因此可以使用其他的数据结构来代替数组结构,也包括了哈希表。红黑树等,以减少JavaScript引擎即时编译生成的机器码存储开销,提高JavaScript引擎在执行时在编译缓存区中查找需要执行的机器码的效率
编译缓存区数据结构的改进与设计
由于编译缓存区只是JavaScript引擎生成的机器码的临时存放区域,JavaScript引擎在执行JavaScript代码是都会将相应的机器码从编译缓冲区拷贝到代码执行池中进行执行,因此只需要保证代码池中的机器码是连续的物理地址,就能使得JavaScript引擎在JavaScript代码时连续的,对于编译缓存区中机器码的存放则不需要保证在物理地址上的连续
因此对于编译缓存区的存储空间采用的是多个离散的基本单元缓存块的方式,可以采取哈希表的数据结构对不同的基本单元缓存块进行管理,而对于哈希表而言,哈希表在输入的数据集随机时容易出现大规模的冲突,虽然有很多有效的哈希函数,但是对于随机输入的数据集时,大规模的冲突还是不可避免的,出现最差的情况
在JavaScript引擎中,由于编译缓存区是一整块连续的物理地址空间,而基本单元缓存块的数量并不会是很多,因此基本单元缓存块的地址可以呈现均匀分布的状态,哈希表就不会出现大规模的冲突状况,而基本单元缓存块之间则用双链表进行链接,保证JavaScript引擎在执行JavaScript代码时能够寻找查找到对应的机器码
由于编译缓存区的基本单元 缓存块是链表结构,因此JavaScript引擎对需要执行的 JavaScript代码所对应的机器码的查找时间复杂度是On。为了提高查找效率,因此可以通过哈希表对编译缓存区的基本单元缓存块进行管理。每次新分配的存入了机器码基本单元缓存块的地址都会更新到哈希表中,而清空一个或多个基本编译缓存块时则会从哈希表中移除。这样JavaScript引擎在查找需要执行的JavaScript代码所对应的机器码时直接通过哈希表进行查找,获取对应的机器码在编译缓存区中的位置,而查找的时间复杂度为O1大大降低了查找开销

CacheHandler结构作为这些独立的基本单元缓存块的管理者,是编译缓存区和的机器码执行的接口。CacheHandler结构使得编译缓存区的管理机制透明,同时与JavaScript引擎现有的编译缓存区的接口一致。JavaScript引擎通过CacheHandler结构提供的接口,就可以实现对即时编译生成的机器码的存储、链接以及将机器码拷贝代码执行池中
class CacheHandler{ public: static CacheHandler* getCacheHandler; bool registernativeCodeNativeCode* nativeCode, size_t& nativekey; NativeCode* getNativeCodesize_t nativekey private: CacheHandler; ~CacheHandler; static CacheHandler* m_cachehandler; size_t m_currentNativeCodeKey; Vector<NativeCode*> m_NativeCodePool;}编译缓存区替换策略的设计
在JavaScript代码的执行过程中JavaScript引擎在执行过程中如果遇到已被编译过的JavaScript代码时,JavaScript引擎会从编译缓存区中查找相应的机器码,将相应的机器码拷贝到代码执行池中,而部分JavaScript代码需要频繁执行,而某些JavaScript代码则不会再次执行。因此可以将这一部分的机器码在编译缓存区中移除,而只需在编译缓存区中保留需要频繁执行的机器码,从而使得编译缓存区的大小固定在一定范围内,减少嵌入式设备的内存压力35。对此,本文根据上文所进行的编译缓存区的存储空间的管理方式和数据结构的改进设计两种编译缓存区的替换策略
基于计数器思想的替换策略
每个基本单元缓存块1到缓存块n中每个都有一个计数器,当某段机器码需要被再次执行时,存储改机器码的一个或者多个基本单元缓存块的计数都加1,如果有即时编译生成的机器码存入编译缓存区时编译缓存区的大小等于JavaScript引擎在启动时所设的固定值时,则需要根据计算器的大小,在将待存入的机器码直接存入到计算取最小的一个或者多个基本单元缓存块中,同时将该基本单元缓存块的计数器置为零
基于LRU算法的替换策略
由于JavaScript引擎将编译缓存区中的各个基本编译缓存块通过链表进行连接,因此可以将各个基本编译缓存块通过双向链表连接起来,当待存入的机器码需要新分配基本编译缓存块存储时,则将新分配的基本编译缓存块置于链表的尾部,当JavaScript引擎需要再次执行某段JavaScript代码时,JavaScript引擎会在查找到所对应的机器吗后将其拷贝到代码池中执行,同时将对应的机器码所在的一个或者多个基本编译缓存块整体迁移到链表的尾部,这样JavaScript代码在经过多次执行后,最近被频繁执行的JavaScript代码对对应的机器码所在的基本编译块向链表表尾移动,执行次数很少或者没有被执行的JavaScript代码所对应的机器码所在的基本编译缓冲块则会向表头移动,当编译缓存区空间等于JavaScript引擎在启动时设定的阈值时,JavaScript引擎则可以将待存入的机器码直接存入到表头中,