0. 介绍

92 阅读4分钟

Java 技术体系包括了以下几个组成部分:

  • Java 程序设计语言
  • 各种硬件平台上的 Java 虚拟机实现
  • Class 文件格式
  • Java 类库 API
  • 来自商业机构和开源社区的第三方 Java 类库 我们可以把 Java 程序设计语言、Java 虚拟机、Java 类库这三部分统称为 JDK(Java Development Kit),JDK 是用于支持 Java 程序开发的最小环境,本文在不产生歧义的地方常以 JDK 来代指整个 Java 技术体系。 可以把 Java 类库 API 中的 Java SE API 子集和 Java 虚拟机这两部分统称为 JRE(Java Runtime Environment),JRE 是支持 Java 程序运行的标准环境。

HotSpot 虚拟机

相信所有 Java 程序员都听说过 HotSpot 虚拟机,它是 Sun/OracleJDK 和 OpenJDK 中的默认 Java 虚拟机,也是目前使用范围最广的 Java 虚拟机。

它名称中的 HotSpot 指的就是它的【热点代码探测技术】,可以通过执行计数器找出最具有编译价值的代码,然后通知即时编译器以方法为单位进行编译。

如果一个方法被频繁调用,或方法中有效循环次数很多,将会分别触发【标准即时编译】和【栈上替换编译】(On-Stack Replacement,OSR)行为。

通过编译器与解释器恰当地协同工作,可以在最优化的程序响应时间与最佳执行性能中取得平衡,而且无须等待本地代码输出才能执行程序,即时编译的时间压力也相对减小,这样有助于引入更复杂的代码优化技术,输出质量更高的本地代码。

向Native迈进

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

在微服务架构的视角下,应用拆分后,单个微服务很可能就不再需要面对数十、数百 GB 乃至 TB 的内存,有了高可用的服务集群,也无须追求单个服务要 7×24 小时不间断地运行,它们随时可以中断和更新;但相应地,Java 的启动时间相对较长,需要预热才能达到最高性能等特点就显得相悖于这样的应用场景。

在最新的几个 JDK 版本的功能清单中,已经陆续推出了跨进程的、可以面向用户程序的类型信息共享 (Application Class Data Sharing,AppCDS,允许把加载解析后的类型信息缓存起来, 从而提升下次启动速度,原本 CDS 只支持 Java 标准库,在 JDK 10 时的 AppCDS 开始 支持用户的程序代码)、无操作的垃圾收集器集器(Epsilon,只做内存分配而不做回收的收,对于运行完就退出的应用十分合适)等改善措施。

而酝酿中的一个更彻底的解决方案,是逐步开始对【提前编译】(Ahead of Time Compilation,AOT)提供支持。

【提前编译】是相对于即时编译的概念,最大好处是 Java 虚拟机加载这些已经预编译成二进制库之后就能够直接调用,而无须再等待即时编译器在运行时将其编译成二进制机器码。

理论上,提前编译可以减少即时编译带来的预热时间,减少Java 应用长期给人带来的“第一次运行慢”的不良体验,可以放心地进行很多全程序的分析行为,可以使用时间压力更大的优化措施

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