对不需要长时间运行的,或者小型化的应用而言,Java(而不是指Java ME)天生就带有一些劣 势,这里并不只是指跑个HelloWorld也需要百多兆的JRE之类的问题,更重要的是指近几年在从大型单 体应用架构向小型微服务应用架构发展的技术潮流下,Java表现出来的不适应。

在微服务架构的视角下,应用拆分后,单个微服务很可能就不再需要面对数十、数百GB乃至TB 的内存,有了高可用的服务集群,也无须追求单个服务要7×24小时不间断地运行,它们随时可以中断 和更新;但相应地,Java的启动时间相对较长,需要预热才能达到最高性能等特点就显得相悖于这样 的应用场景。在无服务架构中,矛盾则可能会更加突出,比起服务,一个函数的规模通常会更小,执 行时间会更短,当前最热门的无服务运行环境AWS Lambda所允许的最长运行时间仅有15分钟。

一直把软件服务作为重点领域的Java自然不可能对此视而不见,在最新的几个JDK版本的功能清 单中,已经陆续推出了跨进程的、可以面向用户程序的类型信息共享(Application Class Data Sharing,AppCDS,允许把加载解析后的类型信息缓存起来,从而提升下次启动速度,原本CDS只支 持Java标准库,在JDK 10时的AppCDS开始支持用户的程序代码)、无操作的垃圾收集器(Epsilon, 只做内存分配而不做回收的收集器,对于运行完就退出的应用十分合适)等改善措施。而酝酿中的一 个更彻底的解决方案,是逐步开始对提前编译(Ahead of Time Compilation,AOT)提供支持。

提前编译是相对于即时编译的概念,提前编译能带来的最大好处是Java虚拟机加载这些已经预编 译成二进制库之后就能够直接调用,而无须再等待即时编译器在运行时将其编译成二进制机器码。理 论上,提前编译可以减少即时编译带来的预热时间,减少Java应用长期给人带来的“第一次运行慢”的 不良体验,可以放心地进行很多全程序的分析行为,可以使用时间压力更大的优化措施[^1]。

但是提前编译的坏处也很明显,它破坏了Java“一次编写,到处运行”的承诺,必须为每个不同的 硬件、操作系统去编译对应的发行包;也显著降低了Java链接过程的动态性,必须要求加载的代码在 编译期就是全部已知的,而不能在运行期才确定,否则就只能舍弃掉已经提前编译好的版本,退回到 原来的即时编译执行状态。

早在JDK 9时期,Java就提供了实验性的Jaotc命令来进行提前编译,不过多数人试用过后都颇感失 望,大家原本期望的是类似于Excelsior JET那样的编译过后能生成本地代码完全脱离Java虚拟机运行的 解决方案,但Jaotc其实仅仅是代替即时编译的一部分作用而已,仍需要运行于HotSpot之上。

直到Substrate VM出现,才算是满足了人们心中对Java提前编译的全部期待。Substrate VM是在 Graal VM 0.20版本里新出现的一个极小型的运行时环境,包括了独立的异常处理、同步调度、线程管 理、内存管理(垃圾收集)和JNI访问等组件,目标是代替HotSpot用来支持提前编译后的程序执行。 它还包含了一个本地镜像的构造器(Native Image Generator),用于为用户程序建立基于Substrate VM 的本地运行时镜像。这个构造器采用指针分析(Points-To Analysis)技术,从用户提供的程序入口出 发,搜索所有可达的代码。在搜索的同时,它还将执行初始化代码,并在最终生成可执行文件时,将 已初始化的堆保存至一个堆快照之中。这样一来,Substrate VM就可以直接从目标程序开始运行,而 无须重复进行Java虚拟机的初始化过程。但相应地,原理上也决定了Substrate VM必须要求目标程序是 完全封闭的,即不能动态加载其他编译器不可知的代码和类库。基于这个假设,Substrate VM才能探索整个编译空间,并通过静态分析推算出所有虚方法调用的目标方法。

Substrate VM带来的好处是能显著降低内存占用及启动时间,由于HotSpot本身就会有一定的内存 消耗(通常约几十MB),这对最低也从几GB内存起步的大型单体应用来说并不算什么,但在微服务 下就是一笔不可忽视的成本。根据Oracle官方给出的测试数据,运行在Substrate VM上的小规模应用, 其内存占用和启动时间与运行在HotSpot上相比有5倍到50倍的下降,具体结果如图1-5和图1-6所示。

Substrate VM补全了Graal VM“Run Programs Faster Anywhere”愿景蓝图里的最后一块拼图,让 Graal VM支持其他语言时不会有重量级的运行负担。譬如运行JavaScript代码,Node.js的V8引擎执行效 率非常高,但即使是最简单的HelloWorld,它也要使用约20MB的内存,而运行在Substrate VM上的 Graal.js,跑一个HelloWorld则只需要4.2MB内存,且运行速度与V8持平。Substrate VM的轻量特性,使 得它十分适合嵌入其他系统,譬如Oracle自家的数据库就已经开始使用这种方式支持用不同的语言代 替PL/SQL来编写存储过程[^2]。在本书第11章还会再详细讨论提前编译的相关内容。

[^1]: 由于AOT编译没有运行时的监控信息,很多由运行信息统计进行向导的优化措施不能使用,所以 尽管没有编译时间的压力,效果也不一定就比JIT更好。
[^2]: Oracle Database MLE,从Oracle 12c开始支持,详见https://oracle.github.io/oracle-db-mle。

1.5.2 新一代即时编译器

对需要长时间运行的应用来说,由于经过充分预热,热点代码会被HotSpot的探测机制准确定位捕 获,并将其编译为物理硬件可直接执行的机器码,在这类应用中Java的运行效率很大程度上取决于即 时编译器所输出的代码质量。

HotSpot虚拟机中含有两个即时编译器,分别是编译耗时短但输出代码优化程度较低的客户端编译 器(简称为C1)以及编译耗时长但输出代码优化质量也更高的服务端编译器(简称为C2),通常它们 会在分层编译机制下与解释器互相配合来共同构成HotSpot虚拟机的执行子系统(这部分具体内容将在 本书第11章展开讲解)。

自JDK 10起,HotSpot中又加入了一个全新的即时编译器:Graal编译器,看名字就可以联想到它 是来自于前一节提到的Graal VM。Graal编译器是以C2编译器替代者的身份登场的。C2的历史已经非 常长了,可以追溯到Cliff Click大神读博士期间的作品,这个由C++写成的编译器尽管目前依然效果拔 群,但已经复杂到连Cliff Click本人都不愿意继续维护的程度。而Graal编译器本身就是由Java语言写 成,实现时又刻意与C2采用了同一种名为“Sea-of-Nodes”的高级中间表示(High IR)形式,使其能够 更容易借鉴C2的优点。Graal编译器比C2编译器晚了足足二十年面世,有着极其充沛的后发优势,在保 持输出相近质量的编译代码的同时,开发效率和扩展性上都要显著优于C2编译器,这决定了C2编译器 中优秀的代码优化技术可以轻易地移植到Graal编译器上,但是反过来Graal编译器中行之有效的优化在 C2编译器里实现起来则异常艰难。这种情况下,Graal的编译效果短短几年间迅速追平了C2,甚至某些 测试项中开始逐渐反超C2编译器。Graal能够做比C2更加复杂的优化,如“部分逃逸分析”(Partial Escape Analysis),也拥有比C2更容易使用激进预测性优化(Aggressive Speculative Optimization)的 策略,支持自定义的预测性假设等。

今天的Graal编译器尚且年幼,还未经过足够多的实践验证,所以仍然带着“实验状态”的标签,需 要用开关参数去激活^1,这让笔者不禁联想起JDK 1.3时代,HotSpot虚拟机刚刚横空出世时的场景, 同样也是需要用开关激活,也是作为Classic虚拟机的替代品的一段历史。

Graal编译器未来的前途可期,作为Java虚拟机执行代码的最新引擎,它的持续改进,会同时为 HotSpot与Graal VM注入更快更强的驱动力。

1.5 展望Java技术的未来

本书第1、2版中的“展望Java技术的未来”分别成文于2011年和2013年,将近十年时间已经过去, 当时畅想的Java新发展新变化全部如约而至,这部分内容已不再有“展望”的价值。笔者在更新第3版时 重写了本节全部内容,并把第2版的“展望”的原文挪到附录之中。倘若Java的未来依旧灿烂精彩,倘若 下一个十年本书还更新第4、第5版,亦希望届时能在附录中回首今日,去回溯哪些预测成为现实,哪 些改进中途夭折。

如1.3节结尾所言,今天的Java正处于机遇与挑战并存的时期,Java未来能否继续壮大发展,某种 程度上取决于如何应对当下已出现的挑战,本文将按照这个脉络来组织,向读者介绍现在仍处于 Oracle Labs中的Graal VM、Valhalla、Amber、Loom、Panama等面向未来的研究项目。

1.5.1 无语言倾向

网上每隔一段时间就能见到几条“未来X语言将会取代Java”的新闻,此处“X”可以用Kotlin、 Golang、Dart、JavaScript、Python等各种编程语言来代入。这大概就是长期占据编程语言榜单第一 位^1的烦恼,天下第一总避免不了挑战者相伴。

如果Java有拟人化的思维,它应该从来没有惧怕过被哪一门语言所取代,Java“天下第一”的底气不 在于语法多么先进好用,而是来自它庞大的用户群和极其成熟的软件生态,这在朝夕之间难以撼动。 不过,既然有那么多新、旧编程语言的兴起躁动,说明必然有其需求动力所在,譬如互联网之于 JavaScript、人工智能之于Python,微服务风潮之于Golang等。大家都清楚不太可能有哪门语言能在每 一个领域都尽占优势,Java已是距离这个目标最接近的选项,但若“天下第一”还要百尺竿头更进一步 的话,似乎就只能忘掉Java语言本身,踏入无招胜有招的境界。

2018年4月,Oracle Labs新公开了一项黑科技:Graal VM,如图1-4所示,从它的口号“Run Programs Faster Anywhere”就能感觉到一颗蓬勃的野心,这句话显然是与1995年Java刚诞生时的“Write Once,Run Anywhere”在遥相呼应。

Graal VM被官方称为“Universal VM”和“Polyglot VM”,这是一个在HotSpot虚拟机基础上增强而成 的跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用,这里“任何语言”包括了Java、Scala、 Groovy、Kotlin等基于Java虚拟机之上的语言,还包括了C、C++、Rust等基于LLVM的语言,同时支 持其他像JavaScript、Ruby、Python和R语言等。Graal VM可以无额外开销地混合使用这些编程语言, 支持不同语言中混用对方的接口和对象,也能够支持这些语言使用已经编写好的本地库文件。

Graal VM的基本工作原理是将这些语言的源代码(例如JavaScript)或源代码编译后的中间格式 (例如LLVM字节码)通过解释器转换为能被Graal VM接受的中间表示(Intermediate Representation, IR),譬如设计一个解释器专门对LLVM输出的字节码进行转换来支持C和C++语言,这个过程称为程 序特化(Specialized,也常被称为Partial Evaluation)。Graal VM提供了Truffle工具集来快速构建面向一 种新语言的解释器,并用它构建了一个称为Sulong的高性能LLVM字节码解释器。

从更严格的角度来看,Graal VM才是真正意义上与物理计算机相对应的高级语言虚拟机,理由是 它与物理硬件的指令集一样,做到了只与机器特性相关而不与某种高级语言特性相关。Oracle Labs的 研究总监Thomas Wuerthinger在接受InfoQ采访时谈到:“随着GraalVM 1.0的发布,我们已经证明了拥 有高性能的多语言虚拟机是可能的,并且实现这个目标的最佳方式不是通过类似Java虚拟机和微软 CLR那样带有语言特性的字节码^2。”对于一些本来就不以速度见长的语言运行环境,由于Graal VM 本身能够对输入的中间表示进行自动优化,在运行时还能进行即时编译优化,因此使用Graal VM实现 往往能够获得比原生编译器更优秀的执行效率,譬如Graal.js要优于Node.js[^3],Graal.Python要优于 CPtyhon[^4],TruffleRuby要优于Ruby MRI,FastR要优于R语言等。

对Java而言,Graal VM本来就是在HotSpot基础上诞生的,天生就可作为一套完整的符合Java SE 8 标准的Java虚拟机来使用。它和标准的HotSpot的差异主要在即时编译器上,其执行效率、编译质量目 前与标准版的HotSpot相比也是互有胜负。但现在Oracle Labs和美国大学里面的研究院所做的最新即时 编译技术的研究全部都迁移至基于Graal VM之上进行了,其发展潜力令人期待。如果Java语言或者 HotSpot虚拟机真的有被取代的一天,那从现在看来Graal VM是希望最大的一个候选项,这场革命很可能会在Java使用者没有明显感觉的情况下悄然而来,Java世界所有的软件生态都没有发生丝毫变化, 但天下第一的位置已经悄然更迭。

[^3]: Graal.js能否比Node.js更快目前为止还存有很大争议,Node.js背靠Google的V8引擎、执行性能优 异,要超越绝非易事。
[^4]: Python的运行环境PyPy其实做了与Graal VM差不多的工作,只是仅针对Python而没有为其他高级 语言提供解释器。

1.4.7 没有成功,但并非失败:Microsoft JVM及其他

在Java虚拟机二十几年的发展历程中,除去上面介绍的那些被大规模商业应用过的Java虚拟机外, 还有许多虚拟机是不为人知地默默沉寂,或者曾经绚丽过但最终夭折湮灭的。我们以其中Microsoft公 司的Java虚拟机为代表来介绍一下。

在Java语言诞生的初期(1996年~1998年,以JDK1.2发布之前为分界),它的主要应用之一是在 浏览器中运行Java Applets程序,微软为了在Internet Explorer 3浏览器中支持Java Applets应用而开发了 自己的Java虚拟机,虽然这款虚拟机只有Windows平台的版本,“一次编译,到处运行”根本无从谈起, 但却是当时Windows系统下性能最好的Java虚拟机,它在1997年和1998年连续获得了《PC Magazine》 杂志的“编辑选择奖”。但是好景不长,在1997年10月,Sun公司正式以侵犯商标、不正当竞争等罪名控 告微软,在随后对微软公司的垄断调查之中,这款虚拟机也曾作为证据之一被呈送法庭。官司的结果 是微软向Sun公司(最终微软因垄断赔偿给Sun公司的总金额高达10亿美元)赔偿2000万美金,承诺终 止其Java虚拟机的发展,并逐步在产品中移除Java虚拟机相关功能。而最令人感到讽刺的是,到后来在 Windows XP SP3中Java虚拟机被完全抹去的时候,Sun公司却又到处登报希望微软不要这样做[^1]。 Windows XP高级产品经理Jim Cullinan称:“我们花费了三年的时间和Sun公司打官司,当时他们试图阻 止我们在Windows中支持Java,现在我们这样做了,可他们又在抱怨,这太具有讽刺意味了。”

我们试想一下,如果当年Sun公司没有起诉微软公司,微软继续保持着对Java技术的热情,那Java 的世界会变得更好还是更坏?.NET技术是否还会发展起来?

[^1]: Sun公司在《纽约时报》《圣约瑟商业新闻》和《华尔街周刊》上刊登了整页的广告,在广告词中 Sun公司号召消费者“要求微软公司继续在其Windows XP系统包括Java平台”。

1.4.6 挑战者:Apache Harmony/Google Android Dalvik VM

这节介绍的Harmony虚拟机(准确地说是Harmony里的DRLVM)和Dalvik虚拟机只能称作“虚拟 机”,而不能称作“Java虚拟机”,但是这两款虚拟机以及背后所代表的技术体系曾经对Java世界产生了 非常大的影响和挑战,当时甚至有悲观的人认为成熟的Java生态系统都有分裂和崩溃的可能。

Apache Harmony是一个Apache软件基金会旗下以Apache License协议开源的实际兼容于JDK 5和 JDK 6的Java程序运行平台,它含有自己的虚拟机和Java类库API,用户可以在上面运行Eclipse、 Tomcat、Maven等常用的Java程序。但是,它并没有通过TCK认证,所以我们不得不用一长串冗长拗 口的语言来介绍它,而不能用一句“Apache的JDK”或者“Apache的Java虚拟机”来直接代指。

如果一个公司要宣称自己的运行平台“兼容于Java技术体系”,那该运行平台就必须要通过 TCK(Technology Compatibility Kit)的兼容性测试,Apache基金会曾要求当时的Sun公司提供TCK的 使用授权,但是一直遭到各种理由的拖延和搪塞,直到Oracle收购了Sun公司之后,双方关系越闹越 僵,最终导致Apache基金会愤然退出JCP组织,这是Java社区有史以来最严重的分裂事件之一。

当Sun公司把自家的JDK开源形成OpenJDK项目之后,Apache Harmony开源的优势被极大地抵 消,以至于连Harmony项目的最大参与者IBM公司也宣布辞去Harmony项目管理主席的职位,转而参 与OpenJDK的开发。虽然Harmony没有真正地被大规模商业运用过,但是它的许多代码(主要是Java 类库部分的代码)被吸纳进IBM的JDK 7实现以及Google Android SDK之中,尤其是对Android的发展 起了很大推动作用。

说到Android,这个时下最热门的移动数码设备平台在最近十年所取得的成果已经远远超越了Java ME在过去二十多年所获得的成果,Android让Java语言真正走进了移动数码设备领域,只是走得并非 Sun公司原本想象的那一条路。

Dalvik虚拟机曾经是Android平台的核心组成部分之一,它的名字来源于冰岛一个名为Dalvik的小 渔村。Dalvik虚拟机并不是一个Java虚拟机,它没有遵循《Java虚拟机规范》,不能直接执行Java的 Class文件,使用寄存器架构而不是Java虚拟机中常见的栈架构。但是它与Java却又有着千丝万缕的联 系,它执行的DEX(Dalvik Executable)文件可以通过Class文件转化而来,使用Java语法编写应用程 序,可以直接使用绝大部分的Java API等。在Android发展的早期,Dalvik虚拟机随着Android的成功迅 速流行,在Android 2.2中开始提供即时编译器实现,执行性能又有了进一步提高。不过到了Android 4.4时代,支持提前编译(Ahead of Time Compilation,AOT)的ART虚拟机迅速崛起,在当时性能还 不算特别强大的移动设备上,提前编译要比即时编译更容易获得高性能,所以在Android 5.0里ART就 全面代替了Dalvik虚拟机。

1.4.5 软硬合璧:BEA Liquid VM/Azul VM

我们平时所提及的“高性能Java虚拟机”一般是指HotSpot、JRockit、J9这类在通用硬件平台上运行 的商用虚拟机,但其实还有一类与特定硬件平台绑定、软硬件配合工作的专有虚拟机,往往能够实现 更高的执行性能,或提供某些特殊的功能特性。这类专有虚拟机的代表是BEA Liquid VM和Azul VM。

Liquid VM也被称为JRockit VE(Virtual Edition,VE),它是BEA公司开发的可以直接运行在自家 Hypervisor系统上的JRockit虚拟机的虚拟化版本,Liquid VM不需要操作系统的支持,或者说它自己本 身实现了一个专用操作系统的必要功能,如线程调度、文件系统、网络支持等。由虚拟机越过通用操 作系统直接控制硬件可以获得很多好处,如在线程调度时,不需要再进行内核态/用户态的切换,这样 可以最大限度地发挥硬件的能力,提升Java程序的执行性能。随着JRockit虚拟机终止开发,Liquid VM 项目也已经停止了。

Azul VM是Azul Systems公司在HotSpot基础上进行大量改进,运行于Azul Systems公司的专有硬 件Vega系统上的Java虚拟机,每个Azul VM实例都可以管理至少数十个CPU和数百GB的内存的硬件资 源,并提供在巨大内存范围内停顿时间可控的垃圾收集器(即业内赫赫有名的PGC和C4收集器),为 专有硬件优化的线程调度等优秀特性。2010年起,Azul公司的重心逐渐开始从硬件转向软件,发布了 自己的Zing虚拟机,可以在通用x86平台上提供接近于Vega系统的性能和一致的功能特性。

随着虚拟机技术的不断发展,Java虚拟机变得越来越强大的同时也越来越复杂,要推动在专有硬 件上的Java虚拟机升级发展,难以直接借助开源社区的力量,往往需要耗费更高昂的成本,在商业上 的缺陷使得专有虚拟机逐渐没落,Azul Systems公司最终也放弃了Vega产品线,把全部精力投入到Zing 和Zulu产品线中。

Zing虚拟机是一个从HotSpot某旧版代码分支基础上独立出来重新开发的高性能Java虚拟机,它可 以运行在通用的Linux/x86-64平台上。Azul公司为它编写了新的垃圾收集器,也修改了HotSpot内的许 多实现细节,在要求低延迟、快速预热等场景中,Zing VM都要比HotSpot表现得更好。Zing的PGC、 C4收集器可以轻易支持TB级别的Java堆内存,而且保证暂停时间仍然可以维持在不超过10毫秒的范围 里,HotSpot要一直到JDK 11和JDK 12的ZGC及Shenandoah收集器才达到了相同的目标,而且目前效 果仍然远不如C4。Zing的ReadyNow!功能可以利用之前运行时收集到的性能监控数据,引导虚拟机 在启动后快速达到稳定的高性能水平,减少启动后从解释执行到即时编译的等待时间。Zing自带的 ZVision/ZVRobot功能可以方便用户监控Java虚拟机的运行状态,从找出代码热点到对象分配监控、锁 竞争监控等。Zing能让普通用户无须了解垃圾收集等底层调优,就可以使得Java应用享有低延迟、快 速预热、易于监控的功能,这是Zing的核心价值和卖点,很多Java应用都可以通过长期努力在应用、 框架层面优化来提升性能,但使用Zing的话就可以把精力更多集中在业务方面。

1.4.4 天下第二:BEA JRockit/IBM J9 VM

前面三节介绍的都是由Sun/Oracle公司研发的Java虚拟机,历史上除了Sun/Oracle公司以外,也有其 他组织、公司开发过虚拟机的实现。如果说HotSpot是天下第一的武林盟主,那曾经与HotSpot并称“三 大商业Java虚拟机”的另外两位,毫无疑问就该是天下第二了,它们分别是BEA System公司的JRockit与 IBM公司的IBM J9。

JRockit虚拟机曾经号称是“世界上速度最快的Java虚拟机”(广告词,IBM J9虚拟机也这样宣传 过,总体上三大虚拟机的性能是交替上升的),它是BEA在2002年从Appeal Virtual Machines公司收购 获得的Java虚拟机。BEA将其发展为一款专门为服务器硬件和服务端应用场景高度优化的虚拟机,由 于专注于服务端应用,它可以不太关注于程序启动速度,因此JRockit内部不包含解释器实现,全部代 码都靠即时编译器编译后执行。除此之外,JRockit的垃圾收集器和Java Mission Control故障处理套件 等部分的实现,在当时众多的Java虚拟机中也处于领先水平。JRockit随着BEA被Oracle收购,现已不再 继续发展,永远停留在R28版本,这是JDK 6版JRockit的代号。

IBM J9虚拟机并不是IBM公司唯一的Java虚拟机,不过目前IBM主力发展无疑就是J9。J9这个名 字最初只是内部开发代号而已,开始选定的正式名称是“IBM Technology for Java Virtual Machine”,简 称IT4J,但这个名字太拗口,接受程度远不如J9。J9虚拟机最初是由IBM Ottawa实验室的一个 SmallTalk虚拟机项目扩展而来,当时这个虚拟机有一个Bug是因为8KB常量值定义错误引起,工程师们 花了很长时间终于发现并解决了这个错误,此后这个版本的虚拟机就被称为K8,后来由其扩展而来、 支持Java语言的虚拟机就被命名为J9。与BEA JRockit只专注于服务端应用不同,IBM J9虚拟机的市场 定位与HotSpot比较接近[^1],它是一款在设计上全面考虑服务端、桌面应用,再到嵌入式的多用途虚 拟机,开发J9的目的是作为IBM公司各种Java产品的执行平台,在和IBM产品(如IBM WebSphere等) 搭配以及在IBM AIX和z/OS这些平台上部署Java应用。

IBM J9直至今天仍旧非常活跃,IBM J9虚拟机的职责分离与模块化做得比HotSpot更优秀,由J9 虚拟机中抽象封装出来的核心组件库(包括垃圾收集器、即时编译器、诊断监控子系统等)就单独构 成了IBM OMR项目,可以在其他语言平台如Ruby、Python中快速组装成相应的功能。从2016年起, IBM逐步将OMR项目和J9虚拟机进行开源,完全开源后便将它们捐献给了Eclipse基金会管理,并重新 命名为Eclipse OMR和OpenJ9[^2]。如果为了学习虚拟机技术而去阅读源码,更加模块化的OpenJ9代码 其实是比HotSpot更好的选择。如果为了使用Java虚拟机时多一种选择,那可以通过AdoptOpenJDK来 获得采用OpenJ9搭配上OpenJDK其他类库组成的完整JDK。

除BEA和IBM公司外,其他一些大公司也号称有自己的专属JDK和虚拟机,但是它们要么是通过 从Sun/Oracle公司购买版权的方式获得的(如HP、SAP等),要么是基于OpenJDK项目改进而来的 (如阿里巴巴、Twitter等),都并非自己独立开发。

[^1]: 严格来说,J9能够支持的市场定位比HotSpot更加广泛,J9最初是为嵌入式领域设计的,后来逐渐 扩展为IBM所有平台共用的虚拟机,嵌入式、桌面、服务器端都用它,而HotSpot在嵌入式领域使用的 是CDC/CLDC以及Java SE Embedded,这也从侧面体现了J9的模块化和通用性做得非常好。
[^2]: 尽管OpenJ9名称上看起来与OpenJDK类似,但它只是一个单独的Java虚拟机,不包括JDK中的其他 内容,实际应该与HotSpot相对应。

1.4.8 百家争鸣

还有一些Java虚拟机天生就注定不会应用在主流领域,或者不是单纯为了用于生产,甚至在设计 之初就没有抱着商用的目的,仅仅是用于研究、验证某种技术和观点,又或者是作为一些规范的标准 实现。这些虚拟机对于大多数不从事相关领域开发的Java程序员来说可能比较陌生,笔者列举几款较 为有影响的:

  • KVM[^1]
    • KVM中的K是“Kilobyte”的意思,它强调简单、轻量、高度可移植,但是运行速度比较慢。在 Android、iOS等智能手机操作系统出现前曾经在手机平台上得到非常广泛应用。
  • Java Card VM
    • JCVM是Java虚拟机很小的一个子集,裁减了许多模块但通常支持绝大多数的常用加密算法。 JCVM必须精简到能放入智能卡、SIM卡、银行信用卡、借记卡内,负责对Java Applet程序进行解释执 行。
  • Squawk VM
    • Squawk VM是由Sun开发,运行于Sun SPOT(Sun Small Programmable Object Tech-nology,一种手 持的Wi-Fi设备),也曾经运用于Java Card。这是一个Java代码比重很高的嵌入式虚拟机实现,其中诸 如类加载器、字节码验证器、垃圾收集器、解释器、编译器和线程调度都是用Java语言完成的,仅仅 靠C语言来编写设备I/O和必要的本地代码。
  • JavaInJava
    • JavaInJava是Sun公司在1997年~1998年间所研发的一个实验室性质的虚拟机,从名字就可以看 出,它试图以Java语言来实现Java语言本身的运行环境,既所谓的“元循环”(Meta-Circular,是指使用 语言自身来实现其运行环境)虚拟机。它必须运行在另外一个宿主虚拟机之上,内部没有即时编译 器,代码只能以解释模式执行。在上世纪末主流原生的Java虚拟机都未能很好解决性能问题的时代, 开发这种项目,其执行速度大家可想而知,不过通过元循环证明一门语言可以自举,是具有它的研究 价值的。
  • Maxine VM
    • Maxine VM和上面的JavaInJava非常相似,它也是一个几乎全部以Java代码实现(只有用于启动 Java虚拟机的加载器使用C语言编写)的元循环Java虚拟机。这个项目于2005年开始,到现在仍然在发 展之中,比起JavaInJava,Maxine VM的执行效率就显得靠谱得多,它有先进的即时编译器和垃圾收集 器,可在宿主模式或独立模式下执行,其执行效率已经接近HotSpot虚拟机Client模式的水平。后来有 了从C1X编译器演进而来的Graal编译器的支持,就更加如虎添翼,执行效率有了进一步飞跃。Graal编 译器现在已经是HotSpot的默认组件,是未来代替HotSpot中服务端编译器的希望。
  • Jikes RVM
    • Jikes RVM是IBM开发的专门用来研究Java虚拟机实现技术的项目。曾用名为Jalapeño。与 JavaInJava和Maxine一样,它也是一个元循环虚拟机。
  • IKVM.NET
    • 这是一个基于微软.NET框架实现的Java虚拟机,并借助Mono获得一定的跨平台能力。IKVM.NET 的目标第一眼看起来的确很奇怪,可能在某些特殊情况下,在.NET上使用某些流行的Java库也许真的 不算是伪需求?IKVM.NET可以将Class文件编译成.NET Assembly,在任意的CLI上运行。

其他在本文中没有介绍到的Java虚拟机还有许多,笔者将自己所知的列举如下:

[^1]: 这里把Java ME里面的虚拟机列为“少数派”是从大多数Java程序员的了解程度出发的,从虚拟机部 署数量来讲,Java ME远比Java SE、Java EE的虚拟机多,毕竟服务器应用是无法在数量上和移动、嵌 入式设备比较的。

1.4.3 小家碧玉:Mobile/Embedded VM

Sun/Oracle公司所研发的虚拟机可不仅包含前面介绍到的服务器、桌面领域的商用虚拟机,面对移 动和嵌入式市场,也有专门的Java虚拟机产品。

由于Java ME产品线的发展相对Java SE来说并不那么成功,所以Java ME中的Java虚拟机相比 HotSpot要低调得多。Oracle公司在Java ME这条产品线上的虚拟机名为CDC-HI(C Virtual Machine, CVM)和CLDC-HI(Monty VM)。其中CDC/CLDC全称是Connected(Limited)Device Configuration,这是一组在JSR-139及JSR-218规范中进行定义的Java API子集,这组规范希望能够在手 机、电子书、PDA等移动设备上建立统一的Java编程接口,CDC-HI VM和CLDC-HI VM就是JSR-139 及JSR-218规范的参考实现,后面的HI则是HotSpot Implementation的缩写,但它们并不是由HotSpot直 接裁剪而来,只是借鉴过其中一些技术,并没有血缘关系,充其量能叫有所渊源。

Java ME中的Java虚拟机现在处于比较尴尬的位置,所面临的局面远不如服务器和桌面领域乐观, 它最大的一块市场——智能手机已被Android和iOS二分天下[^1],现在CDC在智能手机上略微有点声音 的产品是Oracle ADF Mobile,原本它提出的卖点是智能手机上的跨平台(“Developing with Java on iOS and Android”),不过用Java在Android上开发应用还要再安装个CDC虚拟机,这事情听着就觉得别 扭,有多此一举的嫌疑,在iOS上倒确实还有一些人在用。

而在嵌入式设备上,Java ME Embedded又面临着自家Java SE Embedded(eJDK)的直接竞争和侵 蚀,主打高端的CDC-HI经过多年来的扩充,在核心部分其实已经跟Java SE非常接近,能用Java SE的 地方大家自然就不愿意用Java ME,所以市场在快速萎缩,Oracle也基本上砍掉了CDC-HI的所有项 目,把它们都划归到了Java SE Embedded下。Java SE Embedded里带的Java虚拟机当然还是HotSpot,但 这是为了适应嵌入式环境专门定制裁剪的版本,尽可能在支持完整的Java SE功能的前提下向着减少内 存消耗的方向优化,譬如只留下了客户端编译器(C1),去掉了服务端编译器(C2);只保留 Serial/Serial Old垃圾收集器,去掉了其他收集器等。

面向更低端设备的CLDC-HI倒是在智能控制器、传感器等领域还算能维持自己的一片市场,现在 也还在继续发展,但前途并不乐观。目前CLDC中活得最好的产品反而是原本早该被CLDC-HI淘汰的 KVM,国内的老人手机和出口到经济欠发达国家的功能手机(Feature Phone)还在广泛使用这种更加 简单、资源消耗也更小的上一代Java ME虚拟机。

[^1]: 严格来说这种提法并不十分准确,笔者写下这段文字时(2019年),在中国,传音手机的出货量超 过小米、OPPO、VIVO等智能手机巨头,仅次于华为(含荣耀品牌)排行全国第二。传音手机做的是 功能机,销售市场主要在非洲,上面仍然用着Java ME的KVM。

1.4.2 武林盟主:HotSpot VM

相信所有Java程序员都听说过HotSpot虚拟机,它是Sun/OracleJDK和OpenJDK中的默认Java虚拟 机,也是目前使用范围最广的Java虚拟机。但不一定所有人都知道的是,这个在今天看起来“血统纯 正”的虚拟机在最初并非由Sun公司所开发,而是由一家名为“Longview Technologies”的小公司设计;甚 至这个虚拟机最初并非是为Java语言而研发的,它来源于Strongtalk虚拟机,而这款虚拟机中相当多的 技术又是来源于一款为支持Self语言实现“达到C语言50%以上的执行效率”的目标而设计的Self虚拟机, 最终甚至可以追溯到20世纪80年代中期开发的Berkeley Smalltalk上。Sun公司注意到这款虚拟机在即时 编译等多个方面有着优秀的理念和实际成果,在1997年收购了Longview Technologies公司,从而获得了 HotSpot虚拟机。

HotSpot既继承了Sun之前两款商用虚拟机的优点(如前面提到的准确式内存管理),也有许多自 己新的技术优势,如它名称中的HotSpot指的就是它的热点代码探测技术(这里的描写带有“历史由胜 利者书写”的味道,其实HotSpot与Exact虚拟机基本上是同时期的独立产品,HotSpot出现得还稍早一 些,一开始HotSpot就是基于准确式内存管理的,而Exact VM之中也有与HotSpot几乎一样的热点探测 技术,为了Exact VM和HotSpot VM哪个该成为Sun主要支持的虚拟机,在Sun公司内部还争吵过一场, HotSpot击败Exact并不能算技术上的胜利),HotSpot虚拟机的热点代码探测能力可以通过执行计数器 找出最具有编译价值的代码,然后通知即时编译器以方法为单位进行编译。如果一个方法被频繁调 用,或方法中有效循环次数很多,将会分别触发标准即时编译和栈上替换编译(On-Stack Replacement,OSR)行为^1。通过编译器与解释器恰当地协同工作,可以在最优化的程序响应时间与 最佳执行性能中取得平衡,而且无须等待本地代码输出才能执行程序,即时编译的时间压力也相对减 小,这样有助于引入更复杂的代码优化技术,输出质量更高的本地代码。

2006年,Sun陆续将SunJDK的各个部分在GPLv2协议下开放了源码,形成了Open-JDK项目,其中 当然也包括HotSpot虚拟机。HotSpot从此成为Sun/OracleJDK和OpenJDK两个实现极度接近的JDK项目 的共同虚拟机。Oracle收购Sun以后,建立了HotRockit项目来把原来BEA JRockit中的优秀特性融合到 HotSpot之中。到了2014年的JDK 8时期,里面的HotSpot就已是两者融合的结果,HotSpot在这个过程 里移除掉永久代,吸收了JRockit的Java Mission Control监控工具等功能。

得益于Sun/OracleJDK在Java应用中的统治地位,HotSpot理所当然地成为全世界使用最广泛的Java 虚拟机,是虚拟机家族中毫无争议的“武林盟主”。