来看看JavaScript有做哪些内部优化

256 阅读6分钟

注:本文主要研究的是JavaScript自身的语言优化,而非我们代码的优化。

一. JavaScript内存管理

  • 什么是内存 内存(Memory)也被称为内存储器和主存储器,其作用是用于暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据。计算机中所有程序的运行都是在内存中进行的,因此内存的性能对计算机的影响非常大。

  • 内存生命周期

    1. 分配你所需要的内存
    2. 使用分配到的内存(读、写)
    3. 不需要时将其释放\归还
  • 内存使用 使用值的过程实际上是对分配内存进行读取与写入的操作。读取与写入可能是写入一个变量或者一个对象的属性值,甚至传递函数的参数。

  • 内存释放 大多数内存管理的问题都在这个阶段。在这里最艰难的任务是找到“所分配的内存确实已经不再需要了”。它往往要求开发人员来确定在程序中哪一块内存不再需要并且释放它。

二. JavaScript的垃圾回收

1. 垃圾回收的垃圾指的是什么

一般来说没有被引用的对象就是垃圾,就是要被清除, 有个例外如果几个对象引用形成一个环,互相引用,但根访问不到它们,这几个对象也是垃圾,也要被清除。

2. 垃圾回收的必要性

由于字符串、对象和数组没有固定大小,所有当他们的大小已知时,才能对他们进行动态的存储分配。JavaScript程序每次创建字符串、数组或对象时,解释器都必须分配内存来存储那个实体。只要像这样动态地分配了内存,最终都要释放这些内存以便他们能够被再用,否则,JavaScript的解释器将会消耗完系统中所有可用的内存,造成系统崩溃。

3. JavaScript是如何做到垃圾回收的

要了解JavaScript是如何做到垃圾回收的,我们就得来看看JavaScript中得GC算法。

三. JavaScript中的GC算法

1. GC算法的定义与作用

GC是一种机制,垃圾回收器完成具体的工作,算法就是工作时查找和回收所遵循的规则。

工作的内容就是查找垃圾释放空间、回收空间。

2. 常见的GC算法

  • 引用计数 机制:在对象内部添加一个引用数,判断引用数是否为 0 。当引用关系发生变化时,修改引用数,引用数为 0 时,立即回收。

优点:

    1. 发现垃圾时,立即回收(时刻监控引用数)。

    2. 最大限度减少程序暂停(可以立即回收垃圾)。

缺点:

    1. 无法回收循环对象的引用。

    2. 时间开销大(时刻监控引用数)。

  • 标记清除 机制:先遍历所有对象并标记活动对象(即可达的对象),然后再次遍历所有对象,同时清除未标记的对象,去掉被标记对象的标记。最后进行空间回收。

优点:

    1. 可以回收循环对象的引用。

缺点:

    1. 空间碎片化,当一串地址存储了 A, B, C,三个变量,A,C 被清楚。那么剩下 A,C 释放出来的空间长度是固定的。而接下来被添加的变量长度是未知的。很难和 A,C 的长度完全相同,所以空间利用率一定会降低。

  • 标记整理 机制:标记整理可一看作是标记清除的一个增强,同标记清除一样,先遍历所有对象,并标记所有活动对象。然后清除未标记的变量。只是在清除之前要移动将要被清除变量的位置,以便于释放出连续的空间。从而避免标记清除的缺点。 优点:

    1. 减少碎片空间化。

缺点:

    1. 不会立即回收垃圾对象。 2. 移动对象位置,回收效率慢。

四. 认识V8

1. 什么是V8

  • V8是一款主流的JS执行引擎。
  • V8采用即时编译 所以速度很快。
  • V8有内存设限,32位是不超过800MB,64位是不超过1.5GB。
  • 值类型的数据存在栈里由系统回收,一般我们说的垃圾回收都是回收堆里的,也就是复杂数据类型。

2. V8垃圾回收策略

image.png

  • V8采用分代回收的思想。
  • 内存分为新生代存储区、老生代存储区两种。
  • 为了区分新生代老生代 V8内存空间一分为二,左侧存储新生代,右侧存储老生代。
  • 针对不同种类对象采用不同的算法,以达到最高效的处理。

3. V8常用的GC算法

  • 分代回收 新生代和老生代
  • 空间复制 From空间复制到To
  • 标记清除
  • 标记整理
  • 标记增量

4. 新生代对象的垃圾回收

image.png

  • 小空间用于存储新生代对象,64位最大32MB,32位最大16MB。
  • 新生代对象指的是存活时间较短的对象,例如某函数局部作用域中的一些变量。
  • 回收过程采用复制算法+标记整理。
    • 将新生代内存也分成两个大小相等的空间。
    • 使用空间为From,空闲空间为To。
    • 活动对象存储于From空间中 To一直是空闲的。
    • 当From空间应用到一定程度之后 触发GC操作。
    • 标记整理后将活动对象拷贝到To空间中。
    • From空间全部释放,然后与To空间交换就完成了垃圾回收。
  • 新生代对象回收细节:
    • 拷贝过程中有可能出现晋升。
    • 晋升:指的是将新生代对象移动至老生代进行存储。
    • 一般一轮GC后还存活的新生代对象需要晋升。
    • 如果To空间的使用率超过25% ,那么这些对象也需要晋升。
    • 因为如果To空间使用率过大当To变成From后,有可能没有多少剩余内存留给新进来的数据了。

5. 老生代对象的垃圾回收

image.png

  • 大空间用于存储老生代对象, 64位最大1.4GB ,32位最大700MB。
  • 老生代对象指的是存活时间较长的对象,例如全局下的一些变量或者是闭包中的变量
  • 回收过程采用 标记清除+标记整理+标记增量算法。
    • 首先主要是使用标记清除完成垃圾空间的回收。
    • 当有新生代晋升且老生代内存块不足以存储晋升的数据时触发标记整理,整理碎片化的内存。
    • 采用增量标记进行效率优化。
    • 增量标记:标记过程会阻塞代码执行,所以将整个标记过程分成很多次交替的插在执行过程中去标记。

五. 如何做代码层面的优化

我们如何去做代码层面的优化呢,我这里推荐一篇文章:JS性能优化策略