Jvm基础篇-01-Java虚拟机家族起源

1. 走近Java

1.1 概述

Java不仅仅是一门编程语言,它还是一个由一系列计算机软件和规范组成的技术体系,这个技术体系提供了完整的用于软件开发和跨平台部署的支持环境,并广泛应用于嵌入式系统、移动终端、企业服务器、大型机等多种场合,如下图所示。
Jvm基础篇-01-Java虚拟机家族起源_第1张图片
Java能获得如此广泛的认可,除了它拥有一门结构严谨、面向对象的编程语言之外,还有许多不可忽视的优点:

  • 它摆脱了硬件平台的束缚,实现了一次编写,到处运行的理想;
  • 它提供了一种相对安全的内存管理和访问机制,避免了绝大部分内存泄漏指针越界问题;
  • 它实现了热点代码检测和运行时编译及优化,这使得Java应用能随着运行时间的增长而获得更高的性能;
  • 它有一套完善的应用程序接口,还有无数来自商业机构和开源社区的第三方类库来帮助用户实现各种各样的功能……

1.2 Java的技术体系

  • 从广义上讲,Kotlin、Clojure、JRuby、Groovy等运行于Java虚拟机上的编程语言及其相关的程序都属于Java技术体系中的一员。

如果仅从传统意义上来看,JCP官方所定义的Java技术体系包括了以下几个组成部分:

  • Java程序设计语言
  • 各种硬件平台上的Java虚拟机实现
  • Class文件格式
  • Java类库API
  • 来自商业机构和开源社区的第三方Java类库

Java程序设计语言、Java虚拟机、Java类库这三部分统称为JDK(Java Development Kit)

JDK是用于支持Java程序开发的最小环境,可以把Java类库API中的Java SE ApI子集和Java虚拟机这两部分统称为JRE(Java Runtime Environment),JRE是支持Java程序运行的标准环境。
如图所示:
Jvm基础篇-01-Java虚拟机家族起源_第2张图片
以上是根据Java各个组成部分的功能来进行划分

如果按照技术所服务的领域来划分,那Java技术体系可以分为以下四条主要的产品线:

  • Java Card:支持Java小程序(Apolets)运行在小内存设备(如智能卡)上的平台。
  • Java ME(Micro Edition):支持Java程序运行在移动终端(手机、PDA)上的平台,对JavaAPl有所精简,并加入了移动终端的针对性支持,这条产品线在JDK6以前被称为J2ME。有一点读者请勿混淆,现在在智能手机上非常流行的、主要使用Java语言开发程序的Android并不属于Java ME。
  • Java SE(Standard Edition):支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API,这条产品线在JDK6以前被称为J2SE。
  • Java EE(Enterprise Edition):支持使用多层架构的企业应用(如ERP、MIS、CRM应用)的Java平台,除了提供JavaSE API外,还对其做了大量有针对性的扩充,并提供了相关的部署支持,这条产品线在JDK6以前被称为J2EE,在JDK10以后被0racle放弃,捐献给Eclipse基金会管理,此后被称为Jakarta EE。

1.3 Java的发展史

Jvm基础篇-01-Java虚拟机家族起源_第3张图片
从Java的第一个版本诞生到现在已经有二十余年的时间,现在再来回顾一下Java的发展轨迹:

  • 1991年4月,由James Gosling博士领导的绿色计划(Green Project)开始启动,此计划最初的目标是开发一种能够在各种消费性电子产品(如机顶盒、冰箱、收音机等)上运行的程序架构。这个计划的产品就是java语言的前身:Oak(得名于James Gosling办公室外的一棵橡树)。Oak当时在消费品市场上并不算成功,但随着1995年互联网潮流的兴起,Oak迅速找到了最适合自己发展的市场定位并蜕变成为Java语言。

  • 1995年5月23日,Oak语言改名为Java,并且在SunWorld大会上正式发布Java 1.0版本。Java语言第一次提出了“Write Once,Run Anywhere”的口号。

  • 1996年1月23日,JDK1.0发布,Java语言有了第一个正式版本的运行环境。JDK1.0提供了一个纯解释执行的Java虚拟机实现(Sun Classic VM)。JDK1.0版本的代表技术包括:Java虚拟机、Applet、AWT等。

  • 1996年4月,十个最主要的操作系统和计算机供应商声明将在其产品中嵌入Java技术。同年9月,已有大约8.3万个网页应用了Java技术来制作。在1996年5月底,Sun于美国旧金山举行了首届JavaOne大会,从此JavaOne成为全世界数百万Java语言开发者每年一度的技术盛会。

  • 1997年2月19日,Sun公司发布了J DK1.1,Java里许多最基础的技术支撑点(如JDBC等)都是在JDK1.1版本中提出的,JDK1.1版的技术代表有:JAR文件格式、JDBC、JavaBeans、RMI等。Java语言的语法也有了一定的增强,如内部类(Inner Class)和反射(Reflection)都是在这时候出现的。

  • 直到1999年4月8日,JDK1.1一共发布了1.1.0至1.1.8这9个版本。从1.1.4以后,每个JDK版本都有一个属于自己的名字(工程代号),分别为:JDK1.1.4-Sparkler(宝石)、JDK1.1.5-Pumpkin(南瓜)、JDK1.1.6-Abigail(阿比盖尔,女子名)、JDK1.1.7-Brutus(布鲁图,古罗马政治家和将军)和JDK 1.1.8-Chelsea(切尔西,城市名)。

  • 1998年12月4日,JDK迎来了一个里程碑式的重要版本:工程代号为Playground(竞技场)的JDK1.2,Sun在这个版本中把Java技术体系拆分为三个方向,分别是面向桌面应用开发的J2SE(Java 2Platform,Standard Edition)、面向企业级开发的J2EE(Java 2 Platform,Enterprise Edition)和面向手机等移动终端开发的J2ME(Java2Platform,Micro Edition)。在这个版本中出现的代表性技术非常多,如EJB、Java Plugin、Java IDL、Swing等,并且这个版本中Java虚拟机第一次内置了JIT(Just In Time)即时编译器(JDK1.2中曾并存过三个虚拟机,Classic VM、HotSpot VM和Exact VM,其中Exact VM只在Solaris平台出现过;后面两款虚拟机都是内置了JIT即时编译器的,而之前版本所带的Classic VM只能以外挂的形式使用即时编译器)。在语言和API层面上,Java添加了strictfp关键字,Java类库添加了现在Java编码之中极为常用的一系列Collections集合类等。在1999年3月和7月,分别有JDK1.2.1和JDK1.2.2两个小升级版本发布。

  • 1999年4月27日,HotSpot虚拟机诞生。HotSpot最初由一家名为“Long-view Technologies”的小公司开发,由于HotSpot的优异表现,这家公司在1997年被Sun公司收购。HotSpot虚拟机刚发布时是作为JDK1.2的附加程序提供的,后来它成为JDK1.3及之后所有JDK版本的默认Java虚拟机

  • 2000年5月8日,工程代号为Kestrel(美洲红隼)的JDK1.3发布。相对于JDK1.2,JDK1.3的改进主要体现在Java类库上(如数学运算和新的Timer API等),JNDI服务从JDK1.3开始被作为一项平台级服务提供(以前JNDI仅仅是一项扩展服务),使用CORBAIIOP来实现RMI的通信协议,等等。这个版本还对Java2D做了很多改进,提供了大量新的Java2DAPI,并且新添加了JavaSound类库。JDK1.3有1个修正版本JDK1.3.1,工程代号为Ladybird(瓢虫),于2001年5月17日发布。

  • 自从JDK1.3开始,Sun公司维持着稳定的研发节奏:大约每隔两年发布一个JDK的主版本,以动物命名,期间发布的各个修正版本则以昆虫作为工程代号。

  • 2002年2月13日,JDK1.4发布,工程代号为Merlin(灰背隼)。JDK1.4是标志着Java真正走向成熟的一个版本,Compaq、Fujitsu、SAS、Symbian、IBM等著名公司都有参与功能规划,甚至实现自己独立发行的JDK1.4。哪怕是在近二十年后的今天,仍然有一些主流应用能直接运行在JDK1.4之上,或者继续发布能运行在1.4上的版本。JDK1.4同样带来了很多新的技术特性,如正则表达式、异常链、NIO、日志类、XML解析器和XSLT转换器,等等。JDK1.4有两个后续修正版:2002年9月16日发布的工程代号为Grasshopper(蚱蜢)的JDK1.4.1与2003年6月26日发布的工程 代号为Mantis(螳螂)的JDK1.4.2。

  • 2002年前后还发生了一件与Java没有直接关系,但事实上对Java的发展进程影响很大的事件,就是微软的.NET Framework发布。这个无论是技术实现还是目标用户上都与Java有很多相近之处的技术平台给Java带来了很多讨论、比较与竞争,.NET平台和Java平台之间声势浩大的孰优孰劣的论战到今天为止都仍然没有完全平息。

  • 2004年9月30日,JDK5发布,工程代号为Tiger(老虎)。Sun公司从这个版本开始放弃了谦逊的“JDK1.x”的命名方式,将产品版本号修改成了“JDKx”。从JDK1.2以来,Java在语法层面上的变动一直很小,而JDK5在Java语法易用性上做出了非常大的改进。如:自动装箱、泛型、动态注解、枚举、可变长参数、遍历循环(foreach循环)等语法特性都是在JDK5中加入的。在虚拟机和API层面上,这个版本改进了Java的内存模型(Java Memory Model,JMM)、提供了java.util.concurrent并发包等。另外,JDK5是官方声明可以支持Windows 9x操作系统的最后一个JDK版本。

  • 2006年12月11日,JDK6发布,工程代号为Mustang(野马)。在这个版本中,Sun公司终结了从JDK1.2开始已经有八年历史的J2EE、J2SE、J2ME的产品线命名方式,启用Java EE6、Java SE6、JavaME6的新命名来代替。JDK6的改进包括:提供初步的动态语言支持(通过内置Mozilla JavaScript Rhino引擎实现)、提供编译期注解处理器和微型HTTP服务器API,等等。同时,这个版本对Java虚拟机内部做了大量改进,包括锁与同步、垃圾收集、类加载等方面的实现都有相当多的改动。

  • 在2006年11月13日的JavaOne大会上,Sun公司宣布计划要把Java开源,在随后的一年多时间内,它陆续地将JDK的各个部分在GPLv2(GNU General Public License v2)协议下公开了源码,并建立了OpenJDK组织对这些源码进行独立管理。除了极少量的产权代码(Encumbered Code,这部分代码所有权不属于Sun公司,Sun本身也无权进行开源处理)外,OpenJDK几乎拥有了当时SunJDK7的全部代码,OpenJDK的质量主管曾经表示在JDK7中,SunJDK和OpenJDK除了代码文件头的版权注释之外,代码几乎是完全一样的,所以OpenJDK7与SunJDK7本质上就是同一套代码库出来的产品。

  • JDK6发布以后,由于代码复杂性的增加、Java开源、开发JavaFX、世界经济危机及Oracle对Sun的收购案等原因,Sun公司在发展Java以外的事情上耗费了太多精力和资源,JDK的更新没有能够继续维持两年发布一个主版本的研发速度,这导致了JDK6的生命周期异常的长,一共发布了211个更新升级补丁,最后的版本为Java SE6Update 211于2018年10月18日发布。

  • 2009年2月19日,工程代号为Dolphin(海豚)的JDK7完成了其第一个里程碑版本。按照JDK7最初的功能规划,一共会设置十个里程碑。最后一个里程碑版本原计划定于2010年9月9日结束,但由于各种原因,JDK7最终无法按计划完成。

  • 从JDK7最原始的功能清单来看,它本应是一个包含许多重要改进的JDK版本,其中规划的子项目都为Java业界翘首以盼,包括:

    • Lambda项目:支持Lambda表达式,支持函数式编程。
    • Jigsaw项目:虚拟机层面的模块化支持。
    • 动态语言支持:Java是静态语言,为其他运行在Java虚拟机上的动态语言提供支持。
    • Garbage First收集器。Coin项目:Java语法细节进化。
  • 令人惋惜的是,在JDK7开发期间,Sun公司相继在技术竞争和商业竞争中陷入泥潭,公司的股票市值跌至仅有高峰时期的3%,已无力推动JDK7的研发工作按计划继续进行。为了尽快结束JDK7长期跳票的问题,Oracle收购Sun公司后随即宣布马上实行“B计划”,大幅裁剪了JDK7预定目标,以保证JDK7的正式版能够于2011年7月28日准时发布。“B计划”的主要措施是把不能按时完成的Lambda项目、Jigsaw项目和Coin项目的部分改进延迟到JDK8之中。最终,JDK7包含的改进有:提供新的G1收集器(G1在发布时依然处于Experimental状态,直至2012年4月的Update4中才正式商用)、加强对非Java语言的调用支持(JSR-292,这项特性在到JDK11还有改动)、可并行的类加载架构等。

  • Oracle公司接手了JDK开发工作以后,迅速展现出了完全不同于Sun时期的、极具商业化的处事风格。面对Java中使用最广泛而又一直免费的Java SE产品线,Oracle很快定义了一套新的Java SE Support产品计划,把JDK的更新支持作为一项商业服务。JDK7发布的前80个更新仍然免费面向所有用户提供,但后续的其他更新包,用户但只能从“将Java SE升级到Java SE Support”与“将JDK7升级到最新版本”两个选项里挑一个。JDK7计划维护至2022年,迄今(面向付费用户)已发布了超过两百个更新补丁,最新版本为JDK7 Update221。

  • 对于JDK7,还有一点值得提起的是,从JDK7update4起,JavaSE的核心功能正式开始为MacOSX操作系统提供支持,并在JDK7Uupdate6中达到所有功能与MacOSX完全兼容的程度;同时,JDK7Update6还对ARM指令集架构提供了支持。至此,官方提供的JDK可以运行于Windows(不含Windows 9x)、Linux、Solaris和Mac OSX操作系统上,支持ARM、×86、×86-64和SPARC指令集架构,JDK7也是可以支持Windows Xp操作系统的最后一个版本。

  • 2009年4月20日,Oracle宣布正式以74亿美元的价格收购市值曾超过2000亿美元的Sun公司,传奇的Sun Microsystems从此落幕成为历史,Java商标正式划归Oracle所有(Java语言本身并不属于哪间公司所有,它由JCP组织进行管理,尽管在JCP中Sun及后来的Oracle的话语权很大)。由于此前Oracle已经收购了另外一家大型的中间件企业BEA公司,当完成对Sun公司的收购之后,Oracle分别从BEA和Sun手中取得了世界三大商用虚拟机的其中两个:JRockit和HotSpot。当时Oracle宣布要在未来一至两年的时间内,把这两个优秀的java虚拟机合二为一。两者合并的结果只能说差强人意,JRockit的监控工具Java Mission Control被移植到了HotSpot,作为收费功能提供给购买了Java SEAdvanced产品计划的用户,其他功能由于两者架构的差异性明显,HotSpot能够直接借鉴融合的功能寥寥无几。

  • JDK8的第一个正式版本原定于2013年9月发布,最终还是跳票到了2014年3月18日,尽管仍然是没有赶上正点,但比起JDK7那种以年作为计时单位、直接把公司跳崩的研发状况已是大有改善。为了保证日后JDK研发能更顺利地进行,从JDK8开始,Oracle启用JEP(JDK Enhancement Proposals)来定义和管理纳入新版JDK发布范围的功能特性。JDK8提供了那些曾在JDK7中规划过,但最终未能在JDK7中完成的功能,主要包括:

    • JEP126:对Lambda表达式的支持,这让Java语言拥有了流畅的函数式表达能力。
    • JEP104:内置Nashorn JavaScript引擎的支持。
    • JEP150:新的时间、日期API。
    • JEP122:彻底移除HotSpot的永久代。
  • JDK9的故事…哈哈有兴趣可以探索

1.4 Java虚拟机家族

许多Java程序员都会潜意识地把Java虚拟机与Oracle JDK的HotSpot虚拟机等同看待,也许还有一些程序员会注意到BEAJRockit和IBMJ9虚拟机,但绝大多数人对Java虚拟机的认识就仅限于此了。

从1996年初Sun发布的JDK1.0中包含的Sun Classic虚拟机到今天,曾经涌现、湮灭过许多或经典,或优秀,或有特色,或有争议的虚拟机实现,在这一节中,我们仍先把代码与技术放下,一起来回顾Java虚拟机家族的发展轨迹和历史变迁。

1.4.1 虚拟机始祖:Sun Classic/Exact VM

以今天的视角来看,Sun Classic虚拟机的技术已经相当原始,这款虚拟机的使命也早已终结。
但仅凭它“世界上第一款商用Java虚拟机”的头衔,就足够有令历史记住它的理由。

  • 1996年1月23日,Sun发布JDK1.0,Java语言首次拥有了商用的正式运行环境,这个JDK中所带的虚拟机就是CIassic VM。这款虚拟机只能使用纯解释器方式来执行Java代码,如果要使用即时编译器那就必须进行外挂,但是假如外挂了即时编译器的话,即时编译器就会完全接管虚拟机的执行系统,解释器便不能再工作了。在JDK1.2及之前,用户用Classic虚拟机执行java -version命令,将会看到类似下面这行的输出:
java version"1.2.21"
Classic VM(build JDK-1.2.2-001, green threads, sunwjit)
  • 其中的“sunwjit”(Sun WorkshopJ IT)就是Sun提供的外挂编译器,其他类似的外挂编译器还有Symantec JIT和shuJIT等。由于解释器和编译器不能配合工作,这就意味着如果要使用编译执行,编译器就不得不对每一个方法、每一行代码都进行编译,而无论它们执行的频率是否具有编译的价值。基于程序响应时间的压力,这些编译器根本不敢应用编译耗时稍高的优化技术,因此这个阶段的虚拟机虽然用了即时编译器输出本地代码,其执行效率也和传统的C/C++程序有很大差距,“Java语言很慢”的印象就是在这阶段开始在用户心中树立起来的。
  • Sun的虚拟机团队努力去解决Classic虚拟机所面临的各种问题,提升运行效率,在JDK1.2时,曾在Solaris平台上发布过一款名为ExactVM的虚拟机,它的编译执行系统已经具备现代高性能虚拟机雏形,如热点探测、两级即时编译器、编译器与解释器混合工作模式等。
  • Exact VM因它使用准确式内存管理(Exact Memory Management,也可以叫Non-Con-servative/Accurate Mem-ory Management)而得名。准确式内存管理是指虚拟机可以知道内存中某个位置的数据具体是什么类型。譬如内存中有一个32bt的整数123456,虚拟机将有能力分辨出它到底是一个指向了123456的内存地址的引用类型还是一个数值为123456的整数,准确分辨出哪些内存是引用类型,这也是在垃圾收集时准确判断堆上的数据是否还可能被使用的前提。由于使用了准确式内存管理,Exact VM可以抛弃掉以前Classic VM基于句柄(Handle)的对象查找方式(原因是垃圾收集后对象将可能会被移动位置,如果地址为123456的对象移动到654321,在没有明确信息表明内存中哪些数据是引用类型的前提下,那虚拟机肯定是不敢把内存中所有为123456的值改成654321的,所以要使用句柄来保持引用值的稳定),这样每次定位对象都少了一次间接查找的开销,显著提升执行性能。
  • 虽然Exact VM的技术相对Classic VM来说先进了许多,但是它的命运显得十分英雄气短,在商业应用上只存在了很短暂的时间就被外部引进的HotSpot VM所取代,甚至还没有来得及发布Windows和Linux平台下的商用版本。而Classic VM的生命周期则相对要长不少,它在JDK1.2之前是JDK中唯一的虚拟机,在JDK1.2时,它与HotSpotVM并存,但默认是使用Classic VM(用户可用java -hotspot参数切换至HotSpot VM),而在JDK1.3时,HotSpot VM成为默认虚拟机,它仍作为虚拟机的“备用选择”发布(使用java-classic参数切换),直到JDK1.4的时候,Classic VM才完全退出商用虚拟机的历史舞台,与Exact VM一起进入了Sun Labs Research VM之中。

1.4.2 武林盟主:HotSpot VM

  • 相信所有Java程序员都听说过HotSpot虚拟机,它是Sun/OracleJDK和OpenJDK中的默认Java虚拟机,也是目前使用范围最广的Java虚拟机。
    但不一定所有人都知道的是,这个在今天看起来“血统纯正”的虚拟机在最初并非由Sun公司所开发,而是由一家名为“Longview Technologies”的小公司设计;甚至这个虚拟机最初并非是为Java语言而研发的,它来源于Strongtalk虚拟机,而这款虚拟机中相当多的技术又是来源于一款为支持Self语言实现“达到C语言50%以上的执行效率”的目标而设计的Self虚拟机,最终甚至可以追溯到20世纪80年代中期开发的BerkeleySmalltalk上。
    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)行为。通过编译器与解释器恰当地协同工作,可以在最优化的程序响应时间与最佳执行性能中取得平衡,而且无须等待本地代码输出才能执行程序,即时编译的时间压力也相对减小,这样有助于引入更复杂的代码优化技术,输出质量更高的本地代码。

    • 解释: 编译器和解释器的作用和区别
      • Java编译器:将Java源文件,也就是.java文件编译成字节码.class文件(二进制字节码文件)

      • Java解释器:就是把java虚拟机上运行的.class字节码解释成机器指令,让CPU识别运行。Java解释器用来解释执行Java编译器编译后的.class文件。
        Jvm基础篇-01-Java虚拟机家族起源_第4张图片

      • 当程序需要首次启动和执行的时候,解释器可以首次发挥作用,一行一行代码的直接转义执行,但是效率低下。当多次调用方法或者循环体的时候,JIT(即时编译器)就可以发挥作用,把越来越多的代码编译成本地机器码,之后可以获得更高的效率

        JIT是即时编译器 – 在执行本机之前,将给定的字节码指令序列编译为运行时的机器码。以方法为单位,一次性的将整个方法的字节码编译成本地机器码,机器码供CPU运行。它的主要目的是在性能上做大量的优化。

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

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

1.4.3 展望Java技术的未来

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

  • 无语言倾向
    网上每隔一段时间就能见到几条“未来X语言将会取代Java”的新闻,此处“x”可以用Kotlin、Golang、Dart、JavaScript、Python等各种编程语言来代入。这大概就是长期占据编程语言榜单第一位健的烦恼,天下第一总避免不了挑战者相伴。
    如果Java有拟人化的思维,它应该从来没有惧怕过被哪一门语言所取代,Java“天下第一”的底气不在于语法多么先进好用,而是来自它庞大的用户群和极其成熟的软件生态,这在朝夕之间难以撼动。不过,既然有那么多新、旧编程语言的兴起躁动,说明必然有其需求动力所在,譬如互联网之于JavaScript、人工智能之于Python,微服务风潮之于Golang等。大家都清楚不太可能有哪门语言能在每一个领域都尽占优势,Java已是距离这个目标最接近的选项,但若“天下第一”还要百尺竿头更进一步的话,似乎就只能忘掉Java语言本身,踏入无招胜有招的境界。
    2018年4月,Oracle Labs新公开了一项黑科技:Graal VM,从它的口号“Run Programs Faster Anywhere”就能感觉到一颗蓬勃的野心,这句话显然是与1995年Java刚诞生时的“Write Once,Run Anywhere”在遥相呼应。Graal VM被官方称为“universal V M”和“polyglot VM”,这是一个在HotSpot虚拟机基础上增强而成的跨语言全栈虚拟机,可以作为“任何语言”的运行平台使用,这里“任何语言”包括了Java、Scala、Groovy、Kotlin等基于Java虚拟机之上的语言,还包括了C、C++、Rust等基于LLVM的语言,同时支持其他像JavaScript、Ruby、Python和R语言等。Graal VM可以无额外开销地混合使用这些编程语言,支持不同语言中混用对方的接口和对象,也能够支持这些语言使用已经编写好的本地库文件。
    Jvm基础篇-01-Java虚拟机家族起源_第5张图片
    Graal VM的基本工作原理是将这些语言的源代码(例如JavaScript)或源代码编译后的中间格式(例如LLVM字节码)通过解释器转换为能被Graal VM接受的中间表示(Intermediate Representation,IR),譬如设计一个解释器专门对LLVM输出的字节码进行转换来支持C和C++语言,这个过程称为程序特化(Specialized,也常被称为Partial Ev-aluation)。Graal VM提供了Truffle工具集来快速构建面向一种新语言的解释器,并用它构建了一个称为Sulong的高性能LLVM字节码解释器。
    从更严格的角度来看,Graal VM才是真正意义上与物理计算机相对应的高级语言虚拟机,理由是它与物理硬件的指令集一样,做到了只与机器特性相关而不与某种高级语言特性相关。Oracle Labs的研究总监Thomas Wuerthinger在接受InfoQ采访时谈到:“随着GraalVM1.0的发布,我们已经证明了拥有高性能的多语言虚拟机是可能的,并且实现这个目标的最佳方式不是通过类似Java虚拟机和微软CLR那样带有语言特性的字节码。”对于一些本来就不以速度见长的语言运行环境,由于GraalVM本身能够对输入的中间表示进行自动优化,在运行时还能进行即时编译优化,因此使用Graal VM实现往往能够获得比原生编译器更优秀的执行效率,譬如Graal.js要优于Node.js,Graal.Python要优于CPtyhon,TruffleRuby要优于Ruby MRI,FastR要优于R语言等。
    对Java而言,Graal VM本来就是在HotSpot基础上诞生的,天生就可作为一套完整的符合JavaSE8标准的Java虚拟机来使用。它和标准的HotSpot的差异主要在即时编译器上,其执行效率、编译质量目前与标准版的HotSpot相比也是互有胜负。但现在Oracle Labs和美国大学里面的研究院所做的最新即时编译技术的研究全部都迁移至基于Graal VM之上进行了,其发展潜力令人期待。如果Java语言或者HotSpot虚拟机真的有被取代的一天,那从现在看来Graal VM是希望最大的一个候选项,这场革命很可能会在Java使用者没有明显感觉的情况下悄然而来,Java世界所有的软件生态都没有发生丝毫变化,但天下第一的位置已经悄然更迭。
  • 新一代即时编译器
    对需要长时间运行的应用来说,由于经过充分预热,热点代码会被HotSpot的探测机制准确定位捕获,并将其编译为物理硬件可直接执行的机器码,在这类应用中Java的运行效率很大程度上取决于即时编译器所输出的代码质量。
    HotSpot虚拟机中含有两个即时编译器,分别是编译耗时短但输出代码优化程度较低的客户端编译器(简称为C1)以及编译耗时长但输出代码优化质量也更高的服务端编译器(简称为C2),通常它们会在分层编译机制下与解释器互相配合来共同构成HotSpot虚拟机的执行子系统
    自JDK10起,HotSpot中又加入了一个全新的即时编译器:Graal编译器,看名字就可以联想到它是来自于前一节提到的Graal VM。Graal编译器是以C2编译器替代者的身份登场的。C2的历史已经非常长了,可以追溯到Cliff Click大神读博士期间的作品,这个由C++写成的编译器尽管目前依然效果拔群,但已经复杂到连Cliff Click本人都不愿意继续维护的程度。而Graal编译器本身就是由Java语言写成,实现时又刻意与C2采用了同一种名为“SeaofNodes”的高级中间表示(High IR)形式,使其能够更容易借鉴C2的优点。Graal编译器比C2编译器晚了足足二十年面世,有着极其充沛的后发优势,在保持输出相近质量的编译代码的同时,开发效率和扩展性上都要显著优于C2编译器,这决定了C2编译器中优秀的代码优化技术可以轻易地移植到Graal编译器上,但是反过来Graal编译器中行之有效的优化在C2编译器里实现起来则异常艰难。这种情况下,Graal的编译效果短短几年间迅速追平了C2,甚至某些测试项中开始逐渐反超C2编译器。Graal能够做比C2更加复杂的优化,如“部分逃逸分析”(Partial Escape Analysis),也拥有比C2更容易使用激进预测性优化(Aggressive Speculative Op timization)的策略,支持自定义的预测性假设等。
    今天的Graal编译器尚且年幼,还未经过足够多的实践验证,所以仍然带着“实验状态”的标签,需要用开关参数去激活,这让笔者不禁联想起JDK1.3时代,HotSpot虚拟机刚刚横空出世时的场景,同样也是需要用开关激活,也是作为Classic虚拟机的替代品的一段历史。
    Graal编译器未来的前途可期,作为Java虚拟机执行代码的最新引擎,它的持续改进,会同时为HotSpot与Graal VM注入更快更强的驱动力。

2. 附录

2.1 获取openJdk源码

首先确定要使用的JDK版本,OpenJDK6和OpenJDK7都是开源的,源码都可以在它们的主页(http://openjdk.jgva.net/)上找到。OpenJDK6的源码其实是从OpenJDK7的某个基线中引出的,然后剥离JDK1.7相关的代码,从而得到一份可以通过TCK6的JDK1.6实现,因此直接编译OpenJDK7会更加“
原汁原味”一些,其实这两个版本的编译过程差异并不大。
获取源码有两种方式:

  • 一是通过Mercurial代码版本管理工具从Repository中直接取得源码(Repository地址:http://hg.openjdk.jgva.net/jdk7/jdkZ),这是最直接的方式,从版本管理中看变更轨迹比看什么Release Note都来得实在,不过太麻烦了一些,尤其是Mercurial远不如SVN、ClearCase或CVS之类的版本控制工具那样普及;
  • 另外一种就是直接下载官方打包好的源码包了,可以从Source Releases页面(地址:http://download.jgva.net/openjdk/jgk7/)取得打包好的源码,一般来说大概一个月左右会更新一次,虽然不够及时,但的确方便了许多。笔者下载的是OpenJDK 7 Early Access Source Build b121版,2010年12月9日发布的,大概81.7MB,解压出来约308MB。

你可能感兴趣的:(Java-Jvm篇,jvm,java,开发语言)