JVM 制造商 Azul Systems 宣布发布 ReadyNow!,提供了一些列为消除 Java 虚拟机的预热(Warm-up)而设计的特性。预热是应用所有者执行的一种惯常做法,意在给 JVM 足够的时间去了解将哪段代码编译为机器码,从而利用 JIT 编译器优化。ReadyNow! 配备了 Azul 最新版的 Zing Java 运行时,即 5.9 版本。
该公司提到,ReadyNow! 特别适合应用于金融市场,这类市场中的应用对关键时刻的峰值系统性能有要求,比如开盘时,或者遇到量贩或交易行为中无法预料的激增时。
我们先来看一点背景知识:Java 平台有一个众所周知的特性,即在 Java 应用启动并执行时,JVM 把应用编译为可执行的机器码。随着应用继续运行,JVM 将评估应用的执行历史,然后编译应用代码中较为重要的部分,以提高性能。因此,应用的性能会随着时间的推移而改进。
有的应用团队从一开始就要求高性能,在他们之间有一个很常用的实践做法,即通过为应用提供模拟的“mock”数据对其进行预热。据 Azul 介绍,这种实践是有风险的,未必能产生预期的优化。
InfoQ 就 ReadyNow! 采访了 Gil Tene,他是 Azul 的联合创始人与 CTO。
InfoQ:没有预热的优化看上去非常矛盾。你能谈一下 JIT 是如何在不进行预热的情况下实现优化的吗?
Tene:我们去年用了一年的时间分析原因。我们和客户仔细检查了这一问题,并提供了一些可能的解决方案。但是当我们与客户分析这些方案时,我们知道了起作用和不起作用的分别是什么。ReadyNow! 的一个技巧是进行预测性优化;而传统的 JVM 会优化一条路径,我们则是优化两条路径,而且是提前 1% 进行优化,而非在关键时间窗口之间优化。我们添加了激进的类加载(aggressive class loading)机制:发现作用域中的类时就加载它们,但不初始化,因此如果代码不走那条路,就不必先加载该类。而且我们发现,往往有很多时间花在加载类上。现在我们可以提早对类进行初始化,但是初始化顺序是有语义的,所以这需要控制,因此我们通过提供 API 的方式向开发者提供了控制权,这样开发者就可以为 JIT 编译器提供线索,指出要初始化和优化的内容。因此应用代码可以说,我想让 JVM 现在编译这一块,或者我们想让 JVM 丢弃一切,重新编译,比如初始化代码针对错误的分支进行了优化时,我们想重新优化。
在算法交易中通常会观察到一个有趣的场景,有些算法以很高的频率观察市场状况,但交易并不频繁。所以我们关心的代码很少执行。而这恰恰是需要快速运行的代码。开发者希望告诉编译器这段代码是他们所关心的,但是运行不那么频繁。这就是 ReadyNow! 的“编译器指令(compiler directives)”的一个例子。
所有这些我们的产品都已经提供了。未来数月我们会提供预热的“圣杯”。举个例子,比如说昨天业务很繁忙,遇到了很多事,代码都相当平稳地处理掉了。所以为什么不将昨天的优化应用于今天的运行呢?我们正在做的就是这个。这还不是最终的解决方案;它非常适合代码不会变化的特定应用。例如,匹配引擎就是一个非常合适的用例,因为它相当稳定,然而在算法交易系统中,代码天天变,这种优化就是错误的了,在这种情况下,昨天的优化可能行不通。
InfoQ:如果代码正在改变,优化又是如何进行的呢?
Tene:ReadyNow! 的目的就在于此。开发者需要向系统中加入一些优化预热代码,这样就不会发生去优化(deoptimization)了,而且今天我们可以确保不发生去优化,即便是新代码。如果没有历史信息,圣杯也没什么效果。不过不管有没有历史信息,我们的激进的类加载、激进的初始化和激进的编译器 API,仍然是可以工作的。
InfoQ:你提到的“去优化”是什么意思?
Tene:我们知道预热 JVM 很是困难,因为 JIT 编译器会做一些非常有趣的优化。比如,它会不断地监控正在执行的代码,并假设尚未运行的代码永远都不运行。遗憾的是,当人们预热交易系统时,只是运行一些代码,并不能体现所有的东西,所以 JVM 生成的代码会针对这些模拟运行产生不正确的优化。但是之后 JVM 发现执行模式中有些东西改变了,它就必需回退,运行一段时间的解释代码,以收集新的数据指标。这个阶段被称作去优化。
去优化还有其他一些例子。比如,从来没运行过的类现在必须被加载到正在运行的 JVM 中,而加载类是需要时间的。
我们听到一些恐怖的事情是,有人了解了除了真正的交易,都会导致去优化,于是他们就执行一次真正的交易,然后取消掉该交易。想想其风险吧,虽然这类交易可以通过控制使其不进入市场,但是万一出点错,可能就是上百万美元的错误。你知道,什么错误都有可能出现,这是玩火。
InfoQ:你提到该特性是应客户的要求加入的?
Tene:在 Azul,我们经常发现自己置身低延迟 Java 系统中,这是我们遇到的第一个并非用户要求的特性。对于要求的特性,我自己有笔账,这个特性已经被要求了 22 次,这意味着有 22 个不相关的人要求我解决交易预热问题。
InfoQ:感谢接受我们的采访。最后您还有什么要说的吗?
Tene:嗯,还有一个有趣的地方。当人们让我们提供终极解决方案时,我的第一个问题是,为什么想要这样的方案呢?通常的回答是,他们每晚重启系统,而不想预热。所以我问他们:“你们的算法两周都不会变,为什么不让代码一直跑着呢?”他们回答:“因为 GC 延迟!”
和我们合作,我们的无停顿 GC 没有那个问题。我们正在对人们进行再培训,让他们不要再重启系统。所以现在的情况是,他们需要重启的话才重启,而不是因为某些运行原因。
评论