目录
一、彻底认识Java虚拟机
开创世纪:Sun Classic
开创世纪:Exact VM
武林霸主:HotSpot VM
移动端虚拟机:Mobile/Embedded VM
“三大”其二:BEA JRockit/IBM J9 VM
软硬结合:BEA Liquid VM/Azul VM
其他虚拟机
二、Java的未来
无语言倾向
新一代即时编译器
向 Native 迈进
灵活的胖子
语言语法持续增强
其他
一、彻底认识Java虚拟机
开创世纪:Sun Classic
Sun Classic 虚拟机是“世界上第一款商用 Java 虚拟机”,因此,它又被称为虚拟机始祖。
缺点:这款虚拟机只能使用纯解释器方式来执行 Java 代码,如果要使用即时编译器那就必须进行外挂,
但是假如外挂了即时编译器的话,即时编译器就会完全接管虚拟机的执行系统,解释器便不能再工作了。
开创世纪:Exact VM
为解决 Classic 虚拟机所面临的各种问题,提升运行效率,在JDK 1.2时,Exact VM的虚拟机横空出世!
它的编译执行系统已经具备现代高性能虚拟机雏形,如热点探测、两级即时编译器、编译器与解释器混合工作模
式等。
Exact VM 因为它使用准确式内存管理而得名(Exact Memory Management,也可以叫Non-Conservative/
Accurate Memory Management)
何为准确是内存管理?
准确式内存管理是指虚拟机可以知道内存中某个位置的数据具体是什么类型。
在 JDK 1.2 时,它与 HotSpot VM 并存,但默认是使用 Classic VM(用户可用 java-hotspot 参数切换至
HotSpot VM )
在 JDK 1.3 时, HotSpot VM 成为默认虚拟机, Classic VM 仍作为虚拟机的“备用选择”发布(使用 java-
classic 参数切换),直到 JDK 1.4 的时候, Classic VM 完全退出商用虚拟机的历史舞台,与 Exact VM 一起进
入了 Sun Labs Research VM 之中。
武林霸主:HotSpot VM
HotSpot VM 是 Sun/OracleJDK 和 OpenJDK 中的默认 Java 虚拟机,也是目前使用范围最广的 Java 虚拟
机。这个虚拟机最初并非是为 Java 语言而研发的,它来源于 Strongtalk 虚拟机, Sun 公司注意到这款虚拟机在
即时编译等多个方面有着优秀的理念和实际成果,于是,在 1997 年收购了 Longview Technologies 公司,从而
获得了 HotSpot 虚拟机。
我们平时所提及的“高性能Java虚拟机”一般是指HotSpot、JRockit、J9 这类在通用硬件平台上运行的商用
虚拟机。
其实HotSpot与Exact虚拟机基本上是同时期的独立产品,HotSpot出现得还稍早一 些,一开始HotSpot就
是基于准确式内存管理的,而Exact VM之中也有与HotSpot几乎一样的热点探测 技术,为了Exact VM和
HotSpot VM哪个该成为Sun主要支持的虚拟机,在Sun公司内部还争吵过一场,HotSpot击败Exact并不能算技
术上的胜利),HotSpot虚拟机的热点代码探测能力可以通过执行计数器 找出最具有编译价值的代码,然后通知
即时编译器以方法为单位进行编译。
如果一个方法被频繁调 用,或方法中有效循环次数很多,将会分别触发标准即时编译和栈上替换编译(On-
Stack Replacement,OSR)行为。通过编译器与解释器恰当地协同工作,可以在最优化的程序响应时间与最佳
执行性能中取得平衡,而且无须等待本地代码输出才能执行程序,即时编译的时间压力也相对减 小,这样有助于
引入更复杂的代码优化技术,输出质量更高的本地代码。
移动端虚拟机:Mobile/Embedded VM
众所周知,Java不仅有主打服务端的Java SE产品线,在移动端和嵌入式市场也运用广泛。Java ME作为Java
在移动端的产品线,相对Java SE的发展就没那么成功。Oracle公司在Java ME这条产品线上的虚拟机名为CDC-HI
(C Virtual Machine,CVM)和CLDC-HI(Monty VM),相对Java SE产品线的HotSpot虚拟机就要低调很
多,但它们并不是由HotSpot直接裁剪而来,而是借鉴了一些技术,并没有直接的血缘关系。在移动市场被
Android和iOS二分天下的情况下,CDC在智能手机上略微有点声音的产品是Oracle ADF Mobile,不过也只是少
量的市场份额。
而在嵌入式设备上,Java ME Embedded又和自家的Java SE Embedded(eJDK)有着直接的竞争关系,而
用户能用Java SE的地方自然就不会选择用Java ME,所以市场在快速萎缩。Java SE Embedded里带的Java虚拟
机当然还是HotSpot,但这是为了适应嵌入式环境专门定制裁剪的版本,尽可能在支持完整的Java SE功能的前提
下向着减少内存消耗的方向优化,譬如只留下了客户端编译器(C1),去掉了服务端编译器(C2);只保留
Serial/Serial Old垃圾收集器,去掉了其他收集器等。
“三大”其二:BEA JRockit/IBM J9 VM
除了Sun/Oracle研发的Java虚拟机,BEA System公司的JRockit与IBM公司的IBM J9则是除了HotSpot虚拟
机之外的两款主流Java虚拟机了,这三款虚拟机也曾经并称为“ 三大商业Java虚拟机”。
JRockit虚拟机是BEA公司在2002年收购Appeal Virtual Machines公司获得的,这款虚拟机在相当一段时间
内号称为“世界上速度最快的Java虚拟机”。它是专注于服务器硬件和服务端应用场景优化的虚拟机,因此不太关
注程序的启动速度,内部不包含解释器。
虽然如此,JRockit虚拟机的垃圾回收算法和Java Mission Control故障处理套件相比其它所有虚拟机表现更
佳。Oracle在2008年收购BEA之后,一直在推进 HotSpot 与 JRockit 两款各有优势的虚拟机进行融合互补,而
JRockit也永远停留在了R28版本。
J9虚拟机作为“三大商业Java虚拟机”的另外一款,是IBM力推的一款虚拟机。与BEA JRockit只专注于服务
端应用不同,IBM J9虚拟机的市场定位与HotSpot比较接近,主要应用IBM公司自己开发的各种Java产品,应用
方向全面考虑服务端、桌面应用,嵌入式等,再和其他产品(如IBM WebSphere等)搭配以及在IBM AIX和z/OS
这些平台上部署Java应用。
软硬结合:BEA Liquid VM/Azul VM
除了HotSpot、JRockit、J9这类在通用硬件平台上运行的“高性能Java虚拟机”,还有一类运行在特殊硬件
平台专有虚拟机。这类虚拟机往往与特定的硬件平台进行绑定,所以有着更高的执行效率和性能,这类专有虚拟
机的代表是BEA Liquid VM和Azul VM。
Liquid VM也就是现在的JRockit VE(Virtual Edition),它是BEA公司开发的可以直接运行在自家
Hypervisor系统上的JRockit虚拟机的虚拟化版本。Liquid VM自己本身实现了一个专用操作系统的必要功能,如
线程调度、文件系统、网络支持等,这种虚拟机越过操作系统直接调用硬件的方式可以最大发挥底层硬件的能
力,极大地提高了Java代码执行性能。
Azul VM是运行于Azul Systems公司的专有硬件Vega系统上的Java专用虚拟机,在HotSpot基础上改动而
来。每个Azul VM实例都可以管理至少数十个CPU和数百GB的内存的硬件资源,并提供在巨大内存范围内停顿时
间可控的垃圾收集器(即业内赫赫有名的PGC和C4收集器),为专有硬件优化的线程调度等优秀特性。然而随着
虚拟机技术的不断发展,商业上的缺陷是的专用虚拟机逐渐落寞,最终Azul System公司也放弃Vega产品线,把
全部精力投入到Zing和Zulu产品线中。
Zing虚拟机是Azul System公司在Sun还未被收购之前,从HotSpot某旧版代码分支基础上独立出来重新开
发而来。经过多年的内部自研,Azul公司为它编写了新的垃圾回收器,支持几TB量级的堆空间管理,同时也配套
了ZVision/ZVRobot这类的性能监控工具,可以方便用户监控JVM的运行状态,比如锁状态竞争、对象分配过
程、热点代码探测等细节。总的来说,Zing拥有高吞吐低延迟、快速预热、易于监控的诸多优点,这些也是Zing
的核心价值和卖点。
其他虚拟机
除此之外,当然还有许多其他虚拟机,例如:Apache Harmony/Google Android Dalvik VM等其他虚拟机。
二、Java的未来
无语言倾向
2018年4月,Oracle Labs新公开了一项黑科技:Graal VM,这是一个在HotSpot虚拟机基础上增强而成 的
跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用,这里“任何语言”包括了Java、Scala、Groovy、
Kotlin等基于Java虚拟机之上的语言,还包括了C、C++、Rust等基于LLVM的语言,同时支持其他像
JavaScript、Ruby、Python和R语言等。
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才是真正意义上与物理计算机相对应的高级语言虚拟机,理由是它与物理硬
件的指令集一样,做到了只与机器特性相关而不与某种高级语言特性相关
对Java而言,Graal VM本来就是在HotSpot基础上诞生的,天生就可作为一套完整的符合Java SE 8标准的
Java虚拟机来使用。
Graal VM 与 HotSpot 的差异主要在于即时编译,在执行效率、编译质量上两者互有胜负。
参见TIOBE编程语言排行榜:TIOBE Index - TIOBE
新一代即时编译器
HotSpot VM 有两个即时编译器(C1,编译耗时短但输出代码优化程度较低的客户端编译器;C2,编译耗
时长但输出代码优化质量更高的服务端编译器),他们通过分层编译机制与解释器互相配合共同构成 HotSpot
VM 的执行子系统;
Graal 编译器(JDK 10 加入 HotSpot 的全新即时编译器),由 Java 编写,目标是替代 C2 编译器,C2 历
史悠久(早 Graal 编译器 20 年),但过于复杂难以维护;Graal 比 C2 更易于做复杂的优化(如部分逃逸分析,
Partital Escape Analysis),更易于做激进预测性优化(Aggressive Speculative Optimization);
也拥有比C2更容易使用激进预测性优化(Aggressive Speculative Optimization)的策略,支持自定义的预测性
假设等。
Graal编译器为Java虚拟机执行代码的最新引擎,它的持续改进,会同时为HotSpot与Graal VM注入更快更强的驱
动力使用-XX:+UnlockExperimentalVMOptions-XX:+UseJVMCICompiler参数来启用Graal编译器
向 Native 迈进
近几年在从大型单体应用架构向小型微服务应用架构(更甚者如无服务架构)发展的技术潮流下,Java 表现得有
些劣势;
- 跨进程、面向用户程序的类型信息共享(Application Class Data Sharing,App CDS),允许把加载解析后
的类型信息缓存起来,从而提升下次启动速度;JDK 10 开始支持到用户代码级;
- 无操作的垃圾收集器(Epsilon),只做内存分配,不做回收,对运行完就退出的应用十分友好;
- 提前编译(Ahead of Time Compilation,AOT),可以减少即时编译的预热时间,直接加载已经编译好的
二进制库直接调用;但它破坏了一次编写,到处运行的承诺;它并不能生成本地代码,仍需运行在 JVM 之
上;(Substrate VM 给出了一个极小型运行时环境,并通过本地镜像构造达到直接运行目标程序的效果);
灵活的胖子
模块化 & 开放性;
语言语法持续增强
JDK 10,Local-Variable Type Inference,本地类型推断;
JDK 11,Local-Variable Syntax for Lambda Parameters;
JDK 13,Switch Expressions,switch 语句的表达式支持;
JDK 13,Text Blocks,文本块功能;
草拟,Enhanced Enums,常量类绑定数据类型,鞋带额外信息;
草拟,Lambda Leftovers,下划线表示 Lambda 中的匿名参数;
草拟,Pattern Matching for instanceof,instanceof 判断过的类型在条件分支免强转;
Project Loom,提供类似 Golang 的 Groutine 的用户线程,可以更轻量,软件自身进行调度(非直接有操作系统内核调度);
Project Valhalla,提供值类型和基本类型的泛型支持,提供不可变类型和非引用类型的声明;
Project Panama,消弭 JVM 与本地代码之间的界限
(JNI 虽然可以调用本地代码,但其只能说是达到能用的标准,频繁执行的性能开销非常高);
其他
其他知识点,请读者去阅读深入理解Java虚拟机第三版