Chrome 61 发布后,被爆存在安全漏洞,而 Chrome 团队在修复安全漏洞的过程中发现一些漏洞是由 V8 的 escape analysis 引起的,编号为 #765433 #752423 等。

escape analysis 学名是逃逸分析。在编译优化理论中,逃逸分析是一种确定指针动态范围的方法——分析在程序的哪些地方可以访问到指针。

V8 v6.1 及更早版本使用了一个复杂的逃逸分析实现,自从引入以来就产生了许多错误(2333)。此实施已被删除,并在 V8 v6.2 中提供了全新的逃逸分析代码库。

为了保护用户的安全,Chrome 团队在 Chrome 61 中关闭了逃逸分析。关闭逃脱分析会对性能产生负面影响,因为它会禁用某些优化。具体来说,以下 ES2015 功能可能会变慢:

  • 解构(destructuring)
  • for-of 迭代器(for-of iteration)
  • 数组扩展(array spread)
  • 剩余参数(rest parameters)

Chrome 团队的这一举措,使得数以亿计的页面执行速度变慢。

不过可以确认的是,禁用逃逸分析只是临时措施。在 Chrome 62 版本中将使用 V8 6.2 中全新的逃逸分析功能。

目前这个举措只影响到 Chrome 61,根据 Node.js 官方 issue #15564 的讨论,Node 8 不受影响,因为漏洞利用不受信任的 JavaScript 执行,除非在 Node.js 中使用了 eval(),否则 Node.js 运行的代码都是有开发者写的受信代码,Node 8 依然会默认开启逃逸分析特性。

什么是逃逸分析?

在 JavaScript 中,如果从当前函数外部可访问某个对象,则这个分配的对象"逃逸(escape)"了。

一句话概括就是,V8 在JavaScript 堆上分配新对象,使用逃逸分析,当 V8 分析出此对象只在函数内部起作用(和函数有相同的生命周期),则 V8 可以把对象分配到栈上,甚至可以把某些变量分配到寄存器中,把对象作为一个简单的局部变量。

如果对象逃逸了,则必须在堆上分配。

例如,逃逸分析使 V8 能够有效地重写以下代码:

function foo(a, b) {
  const object = { a, b };
  return object.a + object.b;
  // Note: `object` does not escape.
}

...这个代码,可以进行如下优化:

function foo(a, b) {
  const object_a = a;
  const object_b = b;
  return object_a + object_b;
}

object_aobject_b 分配在栈上甚至寄存器上。

逃逸分析除了可以将堆分配转化为栈分配以外,还可以:

  • 同步省略:如果一个对象被发现只能从一个线程被访问到,那么对于这个对象的操作可以不考虑同步。
  • 分离对象或标量替换:有的对象可能不需要作为一个连续的内存结构存在也可以被访问到,那么对象的部分(或全部)可以不存储在内存,而是存储在 CPU 寄存器中。

大部分动态语言都使用逃逸分析进行优化。而 V8 下一个版本将采用新一代逃逸分析技术。


欢迎关注我的公众号,关注前端文章:

03-05 22:55