V8 团队的一个错误,使得整个互联网变慢

V8 团队的一个错误,使得整个互联网变慢

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 依然会默认开启逃逸分析特性。

It's not deoptimized. It's just that one phase of the optimization pipeline is disabled, and this bug only affects Chrome. Node can still turn it on (with an appropriate bugfix in place).

什么是逃逸分析?

在 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_a 和 object_b 分配在栈上甚至寄存器上。

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

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

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

发布于 2017-09-29

文章被以下专栏收录

    V8 引擎介绍以及源码分析,包括 V8、Node.js、Chrome 等。。。关注公众号 justjavac 回复 V8 可查看历史文章