世界上并没有完美的程序,但我们并不因此而沮丧,因为写程序本来就是一个不断追求完美的程。
Java不仅仅是一门编程语言,还是一个由一系列计算机软件和规范形成的技术体系,这 个技术体系提供了完整的用于软件开发和跨平台部署的支持环境,并广泛应用于嵌入式系统、移动终端、企业服务器、大型机等各种场合,如图1-1所示。时至今日,Java技术体系已 经吸引了900多万软件开发者,这是全球最大的软件开发团队。使用Java的设备多达几十亿 台,其中包括11亿多台个人计算机、30亿部移动电话及其他手持设备、数量众多的智能卡, 以及大量机顶盒、导航系统和其他设备。
Java能获得如此广泛的认可,除了它拥有一门结构严谨、面向对象的编程语言之外,还 有许多不可忽视的优点:它摆脱了硬件平台的束缚,实 现 了 次 编 写 ,到处运行”的理想; 它提供了一个相对安全的内存管理和访问机制,避免了绝大部分的内存泄露和指针越界问 题 ;它实现了热点代码检测和运行时编译及优化,这使得Java应用能随着运行时间的增加而 获得更高的性能;它有一套完善的应用程序接口,还有无数来自商业机构和开源社区的第三 方类库来帮助它实现各种各样的功能……Java所带来的这些好处使程序的开发效率得到了很 大的提升。作为一名Java程序员,在编写程序时除了尽情发挥Java的各种优势外,还应该去了解和思考一下Java技术体系中这些技术特性是如何实现的。认识这些技术运作的本质,是 自己思考“程序这样写好不好”的基础和前提。当我们在使用一种技术时,如果不再依赖书本和他人就能得到这些问题的答案,那才算上升到了“不惑”的境界。
从广义上讲,Clojure、JRuby、Groovy等运行于Java虚拟机上的语言及其相关的程序都 属于Java技术体系中的一员。如果仅从传统意义上来看,Sun官方所定义的Java技术体系包括 以下几个组成部分:
我们可以把Java程序设计语言、Java虚拟机、 Java Development K i t ) ,JDK是用于支持Java程序开发的最小环境,在后面的内容中,为了讲解 方 便 ,有一些地方会以JDK来代替整个Java技术体系。另 外 ,可以把Java API类库中的Java SE API子集m*Java虚拟机这两部分统称为JRE ( Java Runtime Environment ) ,JRE是支持Java 程序运行的标准环境。图1-2展示了Java技术体系所包含的内容,以及JDK和JRE所涵盖的范围。
以上是根据各个组成部分的功能来进行划分的,如果按照技术所服务的领域来划分,或者说按照Java技术关注的重点业务领域来划分,Java技术体系可以分为4个平台,分别为:
从第一个Java版本诞生到现在已经有18年的时间了。沧海桑田一瞬间,转眼18年过去 了 ,在图1-3所展示的时间线中,我们看到JDK已经发展到了 1.7版。在这18年里还诞生了无数和Java相关的产品、技术和标准。现在让我们走入时间隧道,从孕育Java语言的时代开 始 ,再来回顾一下Java的发展轨迹和历史变迁。
1991年4月,由James Gosling博士领导的绿色计划( Green Project)开始启动,此计划的 目的是开发一种能够在各种消费性电子产品(如机顶盒、冰箱、收音机等)上运行的程序架构。这个计划的产品就是Java语言的前身: Oak (椽树)。Oak当时在消费品市场上并不算成功,但随着1995年互联网潮流的兴起,Oak迅速找到了最适合自己发展的市场定位并蜕变成 为Java语言。
1995年5月23日,Oak语言改名为Java , 并且在SunWorld大会上正式发布Java 1.0版本。 Java语言第一次提出了“Write Once,Run Anywhere”的口号。
1996年1月23日, JDK 1.0发布,Java语言有了第一个正式版本的运行环境。 JDK 1.0提供 了一个纯解释执行的Java虚拟机实现( Sun Classic VM ) 。 JDK 1.0版本的代表技术包括:Java虚拟机、Applet、AWT等。
1996年4月 ,10个最主要的操作系统供应商申明将在其产品中嵌入Java技术。同年9月 , 已有大约8.3万个网页应用了Java技术来制作。在1996年5月 底 ,Sun公司于美国旧金山举行了首届JavaOne大会,从此JavaOne成为全世界数百万Java语言开发者每年一度的技术盛会。
1997年2月19日,Sun公司发布了JDK 1 .1,Java技术的一些最基础的支撑点(如JDBC 等 )都是在JDK 1.1版本中发布的, JDK 1.1版的技术代表有:JAR文件格式、JDBC、 JavaBeans、RMI。Java语法也有了一定的发展,如内部类( Inner Class )和反射( Reflection ) 都是在这个时候出现的。
直到1999年4月8日, JDK 1 .1 一共发布了1.1.0〜1.1.8九个版本。从1.1.4之后,每个JDK)版本都有一个自己的名字(工程代号),分别为: JDK 1.1.4-Sparkler ( 宝石) 、 JDK 1.1.5- Pumpkin ( 南瓜) 、 JDK 1.1.6-Abigail ( 阿比盖尔,女子名) 、 JDK 1.1.7-Brutus ( 布鲁图,古 罗马政治家和将军)和JDK 1.1.8-Chelsea ( 切尔西,城市名)。
1998年12月4日,JDK迎来了一个里程碑式的版本JDK 1.2,工程代号为Playground(竞技 场 ),Sun在这个版本中把Java技术体系拆分为3个方向,分别是面向桌面应用开发的J2SE ( Java 2 Platform,Standard Edition ) 、面向企业级开发的J2EE ( Java 2 Platform,Enterprise Edition ) 和面向手机等移动终端开发的J2ME ( Java 2 Platforn,Micro Edition )。在这个版本中出现的代表性技术非常多,如EJB、 Java Plug-in、 Java IDL、Swing等 ,并且这个版本中 Java虚拟机第一次内置了JIT ( Just In Time ) 编译器( JDK 1.2中曾并存过3个虚拟机, Classic VM、 HotSpot VM和Exact VM ,其中Exact VM只在Solaris平台出现过;后面两个虚拟机都是内置JIT编译器的,而之前版本所带的Classic VM只能以外挂的形式使用JIT编译器)。在语 言和API级别上 ,Java添加了strictfp关键字与现在Java编码之中极为常用的一系列Collections集合类。在1999年3月和7月 ,分别有JDK 1.2.1和JDK 1.2.2两个小版本发布。
1999年4月27日 ,HotSpot虚拟机发布 ,HotSpot最初由一家名为“Longview Technologies”的小公司开发,因为HotSpot的优异表现,这家公司在1997年被Sun公司收购了。HotSpot虚拟机发布时是作为JDK 1.2的附加程序提供的,后来它成为了JDK 1.3及之后所有版本的Sun JDK的默认虚拟机。
2000年5月8日,工程代号为Kestrel (美洲红隼)的JDK 1.3发布, JDK 1.3相对于JDK 1.2 的改进主要表现在一些类库上(如数学运算和新的Timer API等),JNDI服务从JDK 1.3开始 被作为一项平台级服务提供(以前JNDI仅仅是一项扩展),使用CORBA IIOP来实现RMI的 通信协议,等等。这个版本还对Java 2D做了很多改进,提供了大量新的Java 2D API,并且 新添加了JavaSound类库。 JDK 1.3有1个修正版本JDK 1.3.1,工程代号为Ladybird ( 瓢虫), 于2001年5月17日发布。
自从JDK 1.3开 始 ,Sun维持了一个习惯:大约每隔两年发布一个JDK的主版本,以动物命名 ,期间发布的各个修正版本则以昆虫作为工程名称。
2002年2月13日, JDK 1.4发布,工程代号为Merlin (灰背隼) 。 JDK 1.4是Java真正走向 成熟的一个版本,Compaq、Fujitsu、SAS、Symbian、IBM等著名公司都有参与甚至实现自己独立的JDK 1.4。哪怕是在十多年后的今天,仍然有许多主流应用(Spring、Hibernate、 Struts 等 )能直接运行在JDK 1.4之上 ,或者继续发布能运行在JDK 1.4上的版本。 JDK 1.4同样发布了很多新的技术特性,如正则表达式、异常链、NIO、 日志类、XML解析器和XSLT转换器 等。 JDK 1.4有两个后续修正版:2002年9月16日发布的工程代号为Grasshopper ( 蚱蜢)的 JDK 1.4.1与2003年6月26日发布的工程代号为Mantis ( 螳螂)的JDK 1.4.2。
2002年前后还发生了一件与Java没有直接关系,但事实上对Java的发展进程影响很大的 事件,那就是微软公司的.NET Framework发布了。这个无论是技术实现上还是目标用户上都 与Java有很多相近之处的技术平台给Java带来了很多讨论、比较和竞争,.NET平台和Java平台之间声势浩大的孰优孰劣的论战到目前为止都在继续。
2004年9月30日, JDK 1.5发布,工程代号Tiger ( 老虎)。从JDK 1.2以来,Java在语法层面上的变换一直很小,而JDK 1.5在Java语法易用性上做出了非常大的改进。例如,自动装箱、泛型、动态注解、枚举、可变长参数、遍历循环(foreach循环)等语法特性都是在JDK 1.5中加入的。在虚拟机和API层面上,这个版本改进了Java的内存模型( Java Memory Model,JMM ) 、提供了java.util.concurrent并发包等。另外, JDK 1.5是官方声明可以支持Windows 9x平台的最后一个JDK版本。
2006年12月11日, JDK 1.6发布,工程代号Mustang ( 野马)。在这个版本中,Sun终结了从JDK 1.2开始已经有8年历史的J2EE、J2SE、J2ME的命名方式,启用Java SE 6 、 Java EE 6、 Java ME 6的命名方式。 JDK 1.6的改进包括:提供动态语言支持(通过内置Mozilla JavaScript Rhino引擎实现)、提供编译API和微型HTTP服务器API等。同时,这个版本对Java 虚拟机内部做了大量改进,包括锁与同步、垃圾收集、类加载等方面的算法都有相当多的改动。
在2006年11月13 日的JavaOne大会上 ,Sun公司宣布最终会将Java开源 ,并在随后的一年多时间内,陆续将JDK的各个部分在GPLv2 ( GNU General Public License v2 )协议下公开了源码 ,并建立了OpenJDK组织对这些源码进行独立管理。除了极少量的产权代码( Encumbered Code , 这部分代码大多是Sun本身也无权限进行开源处理的)外 ,OpenJDK/L 乎包括了Sun JDK的全部代码,OpenJDK的质量主管曾经表示,在JDK 1.7中,Sun JDK和 OpenJDK除了代码文件头的版权注释之外,代码基本上完全一样,所以OpenJDK 7与Sun JDK 1.7本质上就是同一套代码库开发的产品。
JDK 1.6发布以后,由于代码复杂性的增加、JDK开源、开发JavaFX、经济危机及Sun收购案等原因,Sun在JDK发展以外的事情上耗费了很多资源,JDK的更新没有再维持两年发布—个主版本的发展速度。 JDK 1.6到目前为止一共发布了37个Update版本 ,最新的版本为Java SE6Update37 , 于2012年10月16日发布。
2009年2月19日,工程代号为Dolphin ( 海豚)的JDK 1.7完成了其第一个里程碑版本。根据JDK 1.7的功能规划,一共设置了 10个里程碑。最后一个里程碑版本原计划于2010年9月9日结束,但由于各种原因, JDK 1.7最终无法按计划完成。
从JDK 1.7最开始的功能规划来看,它本应是一个包含许多重要改进的JDK版本,其中的Lambda项目 (Lambda表达式、函数式编程)、Jigsaw项目 (虚拟机模块化支持)、动态语言支持、GarbageFirst收集器和Coin项目 (语言细节进化)等子项目对于Java业界都会产生深远的影响。在JDK 1.7开发期间,Sun公司由于相继在技术竞争和商业竞争中都陷入泥潭,公司的股票市值跌至仅有高峰时期的3% ,已无力推动JDK 1.7的研发工作按正常计划进行。为了尽快结束JDK 1.7长期“跳票” 的问题 ,Oracle公司收购Sun公司后不久便宣布将实行“B计划”, 大幅裁剪了JDK 1.7预定目标,以便保证JDK 1.7的正式版能够于2011年7月28日准时发布。“B计划”把不能按时完成的Lambda项目、Jigsaw项目和Coin项目的部分改进延迟到JDK 1.8之 中。最终, JDK 1.7的主要改进包括:提供新的G1收集器(G1在发布时依然处于Experimental状态,直至2012年4月的Update 4中才正式“转正”)、加强对非Java语言的调用支持(JSR-292 ,这项特性到目前为止依然没有完全实现定型)、升级类加载架构等。
到目前为止, JDK 1.7已经发布了9个Update版本,最新的Java SE 7 Update 9于2012年10 月16日发布。从Java SE 7 Update 4起 ,Oracle开始支持Mac OS X操作系统,并在Update 6中达到完全支持度 ,同时,在Update 6中还对ARM指令集架构提供了支持。至此,官方提供 的JDK可以运行于Windows ( 不含Windows 9x ) 、Linux、Solaris和Mac OS平台上,支持ARM、x86、x64和Sparc指令集架构类型。
2009年4月20日 ,Oracle公司宣布正式以74亿美元的价格收购Sun公 司 ,Java商标从此正 式归Oracle所有(Java语言本身并不属于哪间公司所有,它由JCP组织进行管理,尽管JCP主要是由Sun公司或者说Oracle公司所领导的)。由于此前Oracle公司已经收购了另外一家大型的中间件企业BEA公司 ,在完成对Sun公司的收购之后,Oracle公司分别从BEA和Sun中取得了目前三大商业虚拟机的其中两个:JRockit和HotSpot,Oracle公司宣布在未来1 〜2年的时间内,将把这两个优秀的虚拟机互相取长补短,最终合二为一。可以预见在不久的将来 ,Java虚拟机技术将会产生相当巨大的变化。
根据Oracle官方提供的信息,JDK 1.8的第一个正式版本将于2013年9月发布,JDK 1.8将 会提供在JDK 1.7中规划过,但最终未能在JDK 1.7中发布的特性,即Lambda表达式、 Jigsaw (很不幸,随后Oracle公司又宣布Jigsaw在JDK 1.8中依然无法完成,需要泣至JDK 1.9 ) 和JDK 1.7中未实现的一部分Coin等。
在2011年的JavaOne大会上,Oracle公司还提到了JDK 1.9的长远规划,希望未来的Java虚 拟机能够管理数以GB计的Java堆,能够更高效地与本地代码集成,并且令Java虚拟机运行时尽可能少人工干预,能够自动调节。
备注:JDK从 1.5版 本 开 始 ,官方在正式文档与宣传上已经不再使用类似JDK 1.5的 命 名 ,只有在 程序员内部使用的开发版本号( Developer Version,例如java-version的输出)中才继续沿用 1.5、1.6、1.7的版本号,而公开版本号( Product Version)则改为JDK 5、JDK 6、JDK 7的命名方式,本书为了行文一致,所有场合统一采用开发版本号的命名方式。
上一节我们从整个Java技术的角度观察了Java技术的发展,许多Java程序员都会潜意识地把它与Sun公司的HotSpot虚拟机等同看待,也许还有一些程序员会注意到BEA JRockit和 IBM J9 ,但对JVM的认识不仅仅只有这些。
从1996年初Sun公司发布的JDK 1.0中所包含的Sun Classic VM到今天,曾经涌现、浬灭过 许多或经典或优秀或有特色的虚拟机实现,在这一节中,我们先暂且把代码与技术放下,一 起来回顾一下Java虚拟机家族的发展轨迹和历史变迁。
以今天的视角来看 , Sun Classic VM的技术可能很原始,这款虚拟机的使命也早已终结。但仅凭它“世界上第一款商用Java虚拟机”的头衔,就足够有让历史记住它的理由。
1996年1月23日,Sun公司发布JDK 1.0,Java语言首次拥有了商用的正式运行环境,这个 JDK中所带的虚拟机就是Classic VM。这款虚拟机只能使用纯解释器方式来执行Java代 码 , 如果要使用JIT编译器 ,就必须进行外挂。但是假如外挂了JIT编译器 ,JIT编译器就完全接管了虚拟机的执行系统,解释器便不再工作了。用户在这款虚拟机上执行java-version命令 ,将会看到类似下面这行输出:
java version"l.2.2"
Classic VM (build JDK-1.2.2-001 ,green threads,sunwjit )
其中的“sunwjit”就是Sun提供的外挂编译器,其他类似的外挂编译器还有Symantec JIT和 shuJIT等。由于解释器和编译器不能配合工作,这就意味着如果要使用编译器执行,编译器就不得不对每一个方法、每一行代码都进行编译,而无论它们执行的频率是否具有编译的价 值。基于程序响应时间的压力,这些编译器根本不敢应用编译耗时稍高的优化技术,因此这个阶段的虚拟机即使用了JIT编译器输出本地代码,执行效率也和传统的C/C++程序有很大差 距 ,“Java语言很慢”的形象就是在这时候开始在用户心中树立起来的。
Sun的虚拟机团队努力去解决Classic VM所面临的各种问题,提升运行效率。在JDK 1.2 时 ,曾在Solaris平台上发布过一款名为Exact VM的虚拟机,它的执行系统已经具备现代高性能虚拟机的雏形:如两级即时编译器、编译器与解释器混合工作模式等。 Exact VM因它使用 准确式内存管理( Exact Memory Management,也可以叫Non-Conservative/Accurate Memory
Management)而得名,即虚拟机可以知道内存中某个位置的数据具体是什么类型。譬如内存中有一个32位的整数123456,它到底是一个reference类型指向123456的内存地址还是一个数值为123456的整数,虚拟机将有能力分辨出来,这样才能在GC ( 垃圾收集)的时候准确判断堆上的数据是否还可能被使用。由于使用了准确式内存管理, Exact VM可以拋弃以前 Classic VM基于handler的对象查找方式(原因是进行GC后对象将可能会被移动位置,如果将地址为123456的对象移动到654321,在没有明确信息表明内存中哪些数据是reference的前提 下 ,虚拟机是不敢把内存中所有为123456的值改成654321的 ,所以要使用句柄来保持 reference值的稳定),这样每次定位对象都少了 一次间接查找的开销,提升执行性能。
虽然Exact VM的技术相对Classic VM来说先进了许多,但是在商业应用上只存在了很短暂的时间就被更为优矣的HotSpot VM所取代,甚至还没来得及发布Windows和Linux平台下的商用版本。而Classic VM的生命周期则相对长了许多,它在JDK 1.2之前是Sun JDK中唯一 的虚拟机,在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 ,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟 机,也是目前使用范围最广的Java虚拟机。但不一定所有人都知道的是,这个目前看起来“血统纯正”的虚拟机在最初并非由Sun公司开发,而是由一家名为“Longview Technologies”的小公司设计的;甚至这个虚拟机最初并非是为Java语言而开发的,它来源于 Strongtalk VM ,而这款虚拟机中相当多的技术又是来源于一款支持Selfi吾言实现“达到C语言 50%以上的执行效率” 的目标而设计的虚拟机,Sun公司注意到了这款虚拟机在JIT编译上有许多优秀的理念和实际效果,在1997年收购了Longview Technologies公司 ,从而获得了HotSpot VM。
HotSpot VM既继承了Sun之前两款商用虚拟机的优点(如前面提到的准确式内存管理 ),也有许多自己新的技术优势,如它名称中的HotSpot指的就是它的热点代码探测技术( 其实两个VM基本上是同时期的独立产品,HotSpot还稍早一些,HotSpot—开始就是准确式 GC,而Exact VM之中也有与HotSpot几乎一样的热点探测。为了Exact VM和HotSpot VM哪个成为Sun主要支持的VM产品 ,在Sun公司内部还有过争论,HotSpot打败Exact并不能算技术上的胜利) , HotSpot VM的热点代码探测能力可以通过执行计数器找出最具有编译价值的代码 ,然后通知JIT编译器以方法为单位进行编译。如果一个方法被频繁调用,或方法中有效 循环次数很多,将会分别触发标准编译和OSR ( 栈上替换)编译动作。通过编译器与解释器恰当地协同工作,可以在最优化的程序响应时间与最佳执行性能中取得平衡,而且无须等待本地代码输出才能执行程序,即时编译的时间压力也相对减小,这样有助于引入更多的代码优化技术,输出质量更高的本地代码。
在2006年的JavaOne大会上,Sun公司宣布最终会把Java开源 ,并在随后的一年,陆续将JDK的各个部分(其中当然也包括了HotSpot VM ) 在GPL协议下公开了源码,并在此基础上建立了OpenJDK。这样 , HotSpot VM便成为了Sun JDK和OpenJDK两个实现极度接近的JDK项目的共同虛拟机。
在2008年和2009年 ,Oracle公司分别收购了BEA公司和Sun公司 ,这样Oracle就同时拥有了两款优秀的Java虚 : JRockit VM和HotSpot VM。Oracle公司宣布在不久的将来(大约应在发布JDK 8的时候)会完成这两款虚拟机的整合工作,使之优势互补。整合的方式大致上是在HotSpot的基础上,移植JRockit的优秀特性,譬如使用JRockit的垃圾回收器与 MissionControl服务 ,使用HotSpot的JIT编译器与混合的运行时系统。
Sun公司所研发的虚拟机可不仅有前面介绍的服务器、桌面领域的商用虚拟机,除此之外 ,Sun公司面对移动和嵌入式市场,也发布过虚拟机产品,另外还有一类虚拟机,在设计之初就没抱有商用的目的,仅仅是用于研究、验证某种技术和观点,又或者是作为一些规范的标准实现。这些虚拟机对于大部分不从事相关领域开发的Java程序员来说可能比较陌生。 Sun公司发布的其他Java虚拟机有:
( 1 ) KVM
KVM中的K是“Kilobyte”的意思,它强调简单、轻量、高度可移植,但是运行速度比较慢。在Android、iOS等智能手机操作系统出现前曾经在手机平台上得到非常广泛的应用。
( 2 ) CDC/CLDC HotSpot Implementation
CDC/CLDC全称是Connected ( Limited ) Device Configuration,在JSR-139/JSR-218规范中进行定义,它希望在手机、电子书、PDA等设备上建立统一的Java编程接口,而CDC-HI VM 和CLDC-HIVM则是它们的一组参考实现。CDC/CLDC是整个Java ME的重要支柱,但从目前Android和iOS二分天下的移动数字设备市场看来,在这个领域中,Sun的虚拟机所面临的局面远不如服务器和桌面领域乐观。
( 3 ) Squawk VM
Squawk VM由Sun公司开发,运行于Sun SPOT ( Sun Small Programmable Object Technology , —种手持的WiFi设备),也曾经运用于Java Card。这是一个Java代码比重很高的嵌入式虚拟机实现,其中诸如类加载器、字节码验证器、垃圾收集器、解释器、编译器和线程调度都是Java语言本身完成的,仅仅靠C语言来编写设备I/O和必要的本地代码。
( 4 ) JavalnJava
JavalnJava是Sun公司于1997年〜1998年间研发的一个实验室性质的虚拟机,从名字就可以看出,它试图以Java语言来实现Java语言本身的运行环境,既所谓的“元循环” (Meta-Circular ,是指使用语言自身来实现其运行环境)。它必须运行在另外一个宿主虚拟机之上 ,内部没有JIT编译器,代码只能以解释模式执行。在20世纪末主流Java虚拟机都未能很好解决性能问题的时代,开发这种项目,其执行速度可想而知。
( 5 ) Maxine VM
Maxine VM和上面的JavalnJava非常相似,它也是一个几乎全部以Java代码实现(只有用于启动JVM的加载器使用C语言编写 )的元循环Java虚拟机。这个项目于2005年开始 ,到现在仍然在发展之中,比起JavaInJava,Maxine VM就显得“靠谱”很 多 ,它有先进的JIT编译器和 垃圾收集器(但没有解释器),可在宿主模式或独立模式下执行,其执行效率已经接近了HotSpot Client VM的水平。
前面介绍了Sun公司的各种虚拟机,除了Sun公司以外,其他组织、公司也研发过不少虚拟机实现,其中规模最大、最著名的就是BEA和IBM公司了。
JRockit VM曾经号称“世界上速度最快的Java虚拟机” (广告词,貌似J9 VM也这样说过),它是BEA公司在2002年从Appeal Virtual Machines公司收购的虚拟机。BEA公司将其发展为一款专门为服务器硬件和服务器端应用场景高度优化的虚拟机,由于专注于服务器端应用 ,它可以不太关注程序启动速度,因此JRockit内部不包含解析器实现,全部代码都靠即时编译器编译运执行。除此之外,JRockit的垃圾收集器和MissionControl服务套件等部分的实现 ,在么多Java虚拟机中也一直处于领先水平。
IBM J9 VM并不是IBM公司唯一的Java虚拟机 ,不过是目前其主力发展的Java虚拟机。 IBM J9 VM原本是内部开发代号,正式名称是“IBM Technology for Java Virtual Machine”,简称IT4J,只是这个名字太拗口了一点,普及程度不如J9。J9 VM最初是由IBM Ottawa实验室一个名为SmallTalk的虚拟机扩展而来的,当时这个虚拟机有一个bug是由8k值定义错误引起 的,工程师花了很长时间终于发现并解决了这个错误,此后这个版本的虚拟机就称为K8了, 后来扩展出支持Java的虚拟机就被称为J9了。与BEA JRockit专注于服务器端应用不同, IBM J9的市场定位与Sun HotSpot比较接近,它是一款设计上从服务器端到桌面应用再到嵌入式都全面考虑的多用途虚拟机,J9的开发目的是作为IBM公司各种Java产品的执行平台,它的主要市场是和IBM产品(如IBM WebSphere等)搭配以及在IBM AIX和z/OS这些平台上部署Java 应用。
我们平时所提及的“高性能Java虚拟机一般是指HotSpot、JRockit、J9这类在通用平台上 运行的商用虚拟机,但其实Azul VM和BEA Liquid VM这类特定硬件平台专有的虚拟机才 是“高性能”的武器。
Azul VM是Azul Systems公司在HotSpot基础上进行大量改进,运行于Azul Systems公司的专有硬件Vega系统上的Java虚拟机 ,每个Azul VM实例都可以管理至少数十个CPU和数百GB内存的硬件资源,并提供在巨大内存范围内实现可控的GC时间的垃圾收集器、为专有硬件优化的线程调度等优秀特性。在2010年 , Azul Systems公司开始从硬件转向软件,发布了自己的ZingJVM,可以在通用x86平台上提供接近于Vega系统的特性。
Liquid VM即是现在的JRockit VE ( Virtual Edition ),它是BEA公司开发的,可以直接运行在自家Hypervisor系统上的JRockit VM的虚拟化版本, Liquid VM不需要操作系统的支持, 或者说它自己本身实现了一个专用操作系统的必要功能,如文件系统、网络支持等。由虚拟机越过通用操作系统直接控制硬件可以获得很多好处,如在线程调度时,不需要再进行内核态/用户态的切换等,这样可以最大限度地发挥硬件的能力,提升Java程序的执行性能。
这节介绍的Harmony VM和Dalvik VM只能称做“虚拟机”,而不能称做“Java虚拟机”,但 是这两款虚拟机(以及所代表的技术体系)对最近几年的Java世界产生了非常大的影响和挑战 ,甚至有些悲观的评论家认为成熟的Java生态系统有崩溃的可能。
Apache Harmony是一个Apache软件基金会旗下以Apache License协议开源的实际兼容于JDK 1.5和JDK 1.6的Java程序运行平台,这个介绍相当拗口。它包含自己的虚拟机和Java库 , 用户可以在上面运行Eclipse、Tomcat、Maven等常见的Java程序 ,但是它没有通过TCK认证 ,所以我们不得不用那么一长串拗口的语言来介绍它,而不能用一句“Apache的JDK”来说明。如果一个公司要宣布自己的运行平台“兼容于Java语言”,那就必须要通过TCK ( Technology Compatibility Kit) 的兼容性测试。Apache基金会曾要求Sun公司提供TCK的 使用授权,但是一直遭到拒绝,直到Oracle公司收购了Sun公司之后,双方关系越闹越僵,最终导致Apache愤然退出JCP ( Java Community Process ) 组织 ,这是目前为止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 VM是Android平台的核心组成部分之一,它的名字来源于冰岛一个名为Dalvik的小渔村。Dalvik VM并不是一个Java虚拟机,它没有遵循Java虚拟机规范,不能直接执行Java的Class文件 ,使用的是寄存器架构而不是JVM中常见的栈架构。但是它与Java又有着千丝万缕的联系,它执行的dex(Dalvik Executable)文件可以通过Class文件转化而来,使用Java语法编写应用程序,可以直接使用大部分的Java API等。目前Dalvik VM随着Android一起处于迅猛发展阶段,在Android2.2中已提供即时编译器实现,在执行性能上有了很大的提高。
在十几年的Java虚拟机发展过程中,除去上面介绍的那些被大规模商业应用过的Java虚拟机外 ,还有许多虚拟机是不为人知的或者曾经“绚丽”过但最终湮灭的。我们以其中微软公司的JVM为例来介绍一下。
也许Java程序员听起来可能会觉得惊讶,微软公司曾经是Java技术的铁杆支持者(也必须承认,与Sun公司争夺Java的控制权,令Java从跨平台技术变为绑定在Windows上的术是微软公司的主要目的)。在Java语言诞生的初期(1996年〜1998年 ,以JDK 1.2发布为分界 ),它的主要应用之一是在浏览器中运行Java Applets程 序 ,微软公司为了在IE3中支持Java Applets应用而开发了自己的Java虚拟机,虽然这款虚拟机只有Windows平台的版本,却是当时Windows下性能最好的Java虚拟机,它在1997年和1998年连续两年获得了《 PC Magazine》杂志的“编辑选择奖”。但好景不长,在1997年10月 ,Sun公司正式以侵犯商标、不 正当竞争等罪名控告微软公司,在随后对微软公司的垄断调查之中,这款虚拟机也曾作为证据之一被呈送法庭。这场官司的结果是微软公司赔偿2000万美金给Sun公司 (最终微软公司因垄断赔偿给Sun公司的总金额高达10亿美元),承诺终止其Java虚拟机的发展,并逐步在产品中移除Java虚拟机相关功能。具有讽刺意味的是,到最后在Windows XP SP3中Java虚拟 机被完全抹去的时候,Sun公司却又到处登报希望微软公司不要这样做 。 Windows XP高级产品经理Jim Cullinan称 :“我们花费了3年的时间和Sun打官司,当时他们试图阻止我们在 Windows中支持Java ,现在我们这样做了,可他们又在抱怨,这太具有讽刺意味了。 ”
我们试想一下,如果当年Sun公司没有起诉微软公司,微软公司继续保持着对Java技术的热情 ,那Java的世界会变得怎么样呢?.NET技术是否会发展起来?但历史是没有假设的。 其他在本节中没有介绍到的Java虚拟机还有(当然,应该还有很多笔者所不知道的):
在2005年 ,Java语言诞生10周年的SunOne技术大会上,Java语言之父James Gosling做了一场题为“Java技术下一个十年”的演讲。笔者不具备James Gosling博士那样高屋建瓴的视角 ,这里仅从Java平台中几个新生的但已经开始展现出蓬勃之势的技术发展点来看一下后续 1 〜2个JDK版本内的一些很有希望的技术重点。
模块化是解决应用系统与技术平台越来越复杂、越来越庞大问题的一个重要途径。无论是开发人员还是产品最终用户,都不希望为了系统中一小块的功能而不得不下载、安装、部署及维护整套庞大的系统。站在整个软件工业化的高度来看,模块化是建立各种功能的标准件的前提。最近几年OSGi技术的迅速发展、各个厂商在JCP中对模块化规范的激烈斗争 , 都能充分说明模块化技术的迫切和重要。
在未来的Java平台中,很可能会对模块化提出语法层面的支持。早在2007年 ,Sun公司就提出过JSR-277 :Java模块系统(JavaModuleSystem),试图建立Java平台的模块化标准, 但受挫于以IBM公司为提 交的 JSR-291 : Java SE动态组件支持(Dynamic Component Support for Java SE , 这实际就是OSGi R4.1 ) 。 由于模块化规范主导权的重要性,Sun公司不能接受一个无法由它控制的规范,在整个Java SE 6期间都拒绝把任何模块化技术内置到JDK 之中。在Java SE 7发展初期,Sun公司再次提交了一个新的规范请求文档JSR-294 : Java编程语言中的改进模块性支持(Improved Modularity Support in the Java Programming Language ) , 尽管这个JSR仍然没有通过,但是Sun公司已经独立于JCP专家组在OpenJDK里建立了 一个名 为Jigsaw ( 拼图)的子项目来推动这个规范在Java平台中转变为具体的实现。Java的模块化 之争目前还没有结束,OSGi已经发布到R5.0版本,而Jigsaw从Java 7延迟至Java 8 ,在2012年 7月又不得不宣布推迟到Java 9 中发布,从这点看来,Sun在这场战争中处于劣势,但无论胜利者是哪一方, Java模块化已经成为一项无法阻挡的变革潮流。
如果读者对Java模块化之争感兴趣,可以参考《深入理解OSGi : Equinox原理、应用与最佳实践》的第1章。
当单一的Java开发已经无法满足当前软件的复杂需求时,越来越多基于Java虚拟机的语言开发被应用到软件项目中,Java平台上的多语言混合编程正成为主流,每种语言都可以针对自己擅长的方面更好地解决问题。试想一下,在一个项目之中,并行处理用Clojge语言编写 ,展示层使用JRuby/Rails , 中间层则是Java ,每个应用层都将使用不同的编程语言来完成 ,而且,接口对每一层的开发者都是透明的,各种语言之间的交互不存在任何困难,就像使用自己语言的原生API—样方便,因为它们最终都运行在一个虚拟机之上。
在最近的几年里,Clqjure、 JRuby、Groovy等新生语言的使用人数不断增长,而运行在Java虚拟机( JVM ) 之上的语言数量也在迅速膨胀,图1-4中列举了其中的一部分。这两点证明混合编程在我们身边已经有所应用并被广泛认可。通过特定领域的语言去解决特定领域的问题是当前软件开发应对日趋复杂的项目需求的一个方向。
除了催生出大量的新语言之外,许多已经有很长历史的程序语言也出现了基于Java虚拟机实现的版本,这样使得混合编程对许多以前使用其他语言的“老”程序员也具备相当大的吸引力 ,软件企业投入了大量资本的现有代码资产也能很好地保护起来。表1-1中列举了常见语言的JVM实现版本。
对这些运行于Java虚拟机之上、Java之外的语言,来自系统级的、底层的支持正在迅速增强,以JSR-292为核心的一系列项目和功能改进(如Da Vinci Machine项目、Nashorn引擎、InvokeDymmic指令、java.lang.nwoke包等), 动Java虚拟机从“Java语言的虚拟机”向“多语言虛拟机”的方向发展。
如今 ,CPU硬件的发展方向已经从高频率转变为多核心,随着多核时代的来临,软件开发越来越关注并行编程的领域。早在JDK 1.5就已经引入java.util.concurrent包实现了一个粗粒度的并发框架。而JDK 1.7中加入的java.util.concurrent forkjoin包则是对这个框架的一次重要扩充。Fork/Join模式是处理并行编程的一个经典方法,如图1-5所示。虽然不能解决所有的问题 ,但是在此模式的适用范围之内,能够轻松地利用多个CPU核心提供的计算资源来协作完 成一个复杂的计算任务。通过利用Fork/Join模式 ,我们能够更加顺畅地过渡到多核时代。
在Java 8中 ,将会提供Lambda支持,这将会极大改善目前Java语言不适合函数式编程的现状 ( 目前 Java 语言使用函数式编程并不是不可以 ,只是会显得很臃肿) ,函数式编程的一个重要优点就是这样的程序天然地适合并行运行,这对Java语言在多核时代继续保持主流语言的地位有很大帮助。
另外,在并行计算中必须提及的还有OpenJDK的子项目Sumatra ,目前显卡的算术运算能力、并行能力已经远远超过了CPU ,在图形领域以外发掘显卡的潜力是近几年计算机发展的方向之一,例如C语言的CUDA。Sumatra项目就是为Java提供使用GPU ( Graphics Processing Units ) 和APU( Accelerated Processing Units ) 运算能力的工具,以运它将会直接提供Java语言层面的API,或者为Lambda和其他JVM语言提供底层的并行运算支持。
在JDK 外围 ,也出现了专为满足并行计算需求的计算框架,如Apache的Hadoop Map/Reduce,这是一个简单易懂的并行框架,能够运行在由上千个商用机器组成的大型集群上 ,并且能以一种可靠的容错方式并行处理TB级别的数据集。另 外 ,还出现了诸如Scala、 Clojure及Erlang等天生就具备并行计算能力的语言。
Java 5曾经对Java语法进行了一次扩充,这次扩充加入了自动装箱、泛型、动态注解、 枚举、可变长参数、遍历循环等语法,使得Java语言的精确性和易用性有了很大的进步。在 Java 7 ( 由于进度压力,许多改进已推迟至Java 8 ) 中 ,对Java语法进行了另一次大规模的扩充。 Sun ( 已被Oracle收购)专门为改进Java语法在OpenJDK中建立了Coin子项目m来统一处理对Java语法的细节修改,如二进制数的原生支持、在switch语句中支持字符串、“< > ”操作符、异常处理的改进、简化变长参数方法调用、面向资源的try-catch-finally语句等都是在 Coin项目之中提交的内容。
除了Coin项目之外,在JSR-335 ( Lambda Expressions for the Java TM Programming Language ) 中定义的Lambda表达式也将对Java的语法和语言习惯产生很大的影响,面向函数方式的编程可能会成为主流。
在几年之前,主流的CPU就开始支持64位架构了。Java虚拟机也在很早之前就推出了支持64位系统的版本。但Java程序运行在64位虚拟机上需要付出比较大的额外代价:首先是内存问题,由于指针膨胀和各种数据类型对齐补白的原因,运行于64位系统上的Java应用需要消耗更多的内存,通常要比32位系统额外增加10%〜30%的内存消耗;其次,多个机构的测试结果显示,64位虚拟机的运行速度在各个测试项中几乎全面落后于32位虚拟机,两者大约有15%左右的性能差距。
但是在Java EE方面,企业级应用经常需要使用超过4GB的内存,对于64位虚拟机的需求是非常迫切的,但由于上述原因,许多企业应用都仍然选择使用虚拟集群等方式继续在32位 虚拟机中进行部署。Sun也注意到了这些问题,并做出了一些改善,在JDK 1.6 Update 14之 后,提供了普通对象指针压缩功能(-XX : +UseCompressedOops,这个参数不建议显式设置 ,建议维持默认由虚拟机的Ergonomics机制自动开启),在执行代码时,动态植入压缩指令以节省内存消耗,但是开启压缩指针会增加执行代码数量,因为所有在Java堆里的、指向Java堆内对象的指针都会被压缩,这些指针的访问就需要更多的代码才可以实现,而且并不只是读写字段才受影响,在实例方法调用、子类型检查等操作中也受影响,因为对象实例指向对象类型的引用也被压缩了。随着硬件的进一步发展,计算机终究会完全过渡到64位的时代 ,这是一件毫无疑问的事情,主流的虚拟机应用也终究会从32位发展至64位 ,而虚拟机对64位的支持也将会进一步完善。