虚拟机--第一章走进java--(抄书)

这是本人阅读周志明老师的《深入理解Java虚拟机》第二版抄写的,有很多省略,不适合直接阅读,需要阅读请出门左转淘宝,右转京东,支持周老师(侵权请联系删除)

第一章走近java

世界上并没有完美的程序,但我们并不因此而沮丧,因为写程序本来就是一个不断追求完美的过程。

1.1概述

java不仅仅是一门编程语言,还是一个由一系列计算机软件和规范形成的技术体系,这个技术体系提供了完整的用于软件开发和跨平台部署的支持环境,并广泛应用于嵌入式系统、移动终端、企业服务器、大型机等各种场合。时至今日,java技术体系已经吸引了900多万软件开发者,这是全球最大的软件开发团队。使用java技术体系的设备多达几十亿台,其中包括11亿多台个人计算机‘30亿部移动电话及其他手持设备、数量众多的只能卡、以及大量机顶盒、导航系统和其他设备。

java能获得如此广泛的认可,除了它有一门结构严谨、面向对象的编程语言之外,还有许多不可忽视的优点:它摆脱了硬件平台的束缚,实现了“一次编写,到处运行”的理想;它提供了一个相对安全的内存管理和访问机制,避免了绝大部分的内存泄漏和指针越界问题;它实现了热点代码检测和运行时编译及优化,这使得java应用能随着运行时间的增加而获得更高的性能;它有一套完善的应用程序接口,还有无数来自商业机构和开源社区的第三方类库来帮助它实现各种各样的功能……java所带来的这些好处使程序的开发效率得到了很大的提升。作为一名java程序员,在编写程序时除了尽情发货java的各种优势外,还应该去了解和思考一下java技术体系中这些技术特性是如何实现的。认识这些技术运作的本质,是自己思考“程序这样写好不好”的基础和前提。当我们在使用一种技术时,如果不再依赖书本和他人就能得到这些问题的答案,那才算上升到了“不惑”的境界。

1.2java技术体系

从广义上讲,Clojure、JRuby、Groovy等运行与java虚拟机上的语言及其相关的程序都属于java技术体系中的一员。如果仅从传统意义上来看,Sun官方所定义的java技术体系包括以下几个组成部分:

  • java程序设计语言
  • 各种硬件平台上的java虚拟机
  • Class文件格式
  • Java API类库
  • 来自商业机构和开源社区的第三方java类库
    我们可以把java程序设计语言、java虚拟机、javaAPI类库这三部分统称为JDK,JDK是用于支持java程序开发的最小环境,在后面的内容中,为了讲解方便,有一些地方会以JDK来代替整个java技术体系。另外,可以把java API类库中的java SE API子集和java虚拟机这两部分统称为JRE,JRE是支持java程序运行的标准环境。

按照java技术关注的重点业务领域来划分,java技术体系可以划分为4个平台,可以分为:

  • java Card:支持一些java小程序(Applets)运行在小内存设备(如智能卡)上的平台。
  • java Me:支持java程序运行在移动终端(手机,PDA)上的平台,对java API有所精简,并加入了针对移动终端的支持,这个版本以前称为J2ME
  • java SE:支持面向桌面级应用(如Windows下的应用程序)的java平台,提供了完整的java核心API,这个版本以前称为J2SE。
  • java EE:支持使用多层架构的企业应用的java平台,除了提供java SE API外,还对其做了大量的扩充并提供了相关的部署支持,这个版本以前称为J2EE。

1.3java发展史

从第一个java版本诞生到现在已经有25年的时间了。沧海桑田一瞬间,转眼25年过去了,我们看到JDK已经发展到了11版。在25年里还诞生了无数和java相关的产品、技术和标准。现在让我们走入时间隧道,从孕育java语言的时代开始,再来回顾一下java的发展轨迹和历史变迁。
1991年4月,由James Gosling博士领导的绿色计划开始启动,此计划的目的是开发一种能够在各种消费性电子产品(如机顶盒、冰箱、收音机等)上运行的程序架构。这个计划的产品就是java语言的前身:Oak(橡树)。Oak当时在消费品市场上并不算成功,但随着1995年互联网潮流的兴起,Oak迅速找到了最适合自己发展的市场定位并蜕变成为java语言。
1995年5月23日,Oak语言改名为java,并在SunWord打回上正式公布java1.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月,10个最主要的操作系统供应商申明将在其产品中嵌入java技术。同年9月,已有大约8.3万个网页应用了java技术来制作。在1996年5月底,Sun公司在美国旧金山举行了首届JavaOne大会,从此JavaOne成为全世界数百万java语言开发者每年一度的技术盛会。
1997年2月19日,Sun公司发布了JDK1.1,java技术的一些最基础的支撑点(如JDBC等)都是在JDK1.1版本中发布的,JDK1.1版的技术代表有:JAR文件格式、JDBC、JavaBean、RMI。java语法也有了一定的发展,如内部类和反射都是在这个时候出现的
1999年4月8日,JDK1.1一共发布了1.1.0~1.1.8九个版本。从1.1.4之后,每个JDK版本都有一个自己的名字(工程代号)。。。。
1998年12月4日,JDK迎来了一个里程碑式的版本JDK1.2,工程代号为Playground(竞技场),sun在这个版本中把java技术体系拆分为3个方向,分别是面向桌面的J2SE、面向企业级应用的J2EE和面向手机等移动终端的J2ME。在这个版本中出现的代表性的技术非常多,如EJB、javaPlug-in、java IDL、Swing等,并且在这个版本中java虚拟机第一次内置了JIT编译器(JDK1.2中曾并存过3个虚拟机,Classic VM、HotSpot VM、Exact VM),其中Exact VM只在Solaris平台出现过;后面两个虚拟机都是内置JIT编译器,而之前版本所带的Classic VM只能以外挂的形式使用JIT编译器)。在语言和API级别上,java添加了strict分片关键字与现在java编码之中极为常用的一系列Collection集合类。在1999年3月和7月,分别有JDK1.2.1和JDK1.2.2两个小版本发布。
1999年4月27日,HotSpot虚拟机发布,HotSpot最初由一家名为“longview Technologies”的小公司开发,因为HotSpot的优异表现,这家公司在1997年被Sun公司收购了。HotSpot虚拟机发布时是作为JDK1.2的附加程序提供的,后来他成为了JDK1.3及之后所有版本的Sun JDK的默认虚拟机。
2000年5月8日,工程代号为Kestrel(美洲红隼)的JDK1.3发布,JDK1.3相对于JDK1.2的改进主要表现在一些类库上(如数学运算和新的Tiner API等),JNDI服务从JDK1.3开始被作为一项平台级服务提供(之前只是一项扩展),使用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日,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的各个部分在GPL v2(GNU General Public License v2)协议下公开了源码,并建立了OpenJDK组织对这些源码进行独立管理。除了极少量的产权代码(Encumbered Code,这部分代码大多是Sun本身也无权限进行开源处理的)外,OpenJDK几乎包括了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 SE 6 Update 37,于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表达式、函数式编程)(jdk8中得以实现)、**Jigsaw项目(虚拟机模块化支持)(JDK9中得以实现) **、动态语言支持、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,这项特性到目前为止依然没有完全实现定型)、升级类加载架构等。
到2012年为止,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虚拟机技术将会产生相当巨大的变化。
2014年JDK8发布
2017年JDK9发布
2018年3月JDK10发布
2018年9月JDK11发布

java虚拟机发展史

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

1.4.1 Sun Classic/Exact Vm

以今天的视角来看,SunClassic VM的技术可能很原始,这款虚拟机的使命也早以终结。但仅凭它“世界上第一款商用java虚拟机”的头衔,就足够有让历史记住它的理由。
1996年1月23日,Sun公司发布JDK1.0,java语言首次拥有了商用的正式运行环境,这个JDK中所带的虚拟机就是Classic VM。这款虚拟机只能使用纯解释器方式来执行java代码,如果要使用JIT编译器,就必须进行外挂。但如果外挂了JIT编译器,JIT编译器就完全接管了虚拟机的执行系统,解释器便不再工作了。用户在这款虚拟机上执行java-version命令,将会看到类似下面这行输出:
java version “1.2.3”
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之中。

1.4.2 Sun HotSpot Vm

提起HotSpot VM,相信所有Java程序员都知道,它是Sun JDK和OpenJDK中所带的虚拟机,也是目前使用范围最广的Java虚拟机。但不一定所有人都知道的是,这个目前看起来“血统纯正”的虚拟机在最初并非由Sun公司开发,而是由一家名为“Longview Technologies”的小公司设计的;甚至这个虚拟机最初并非是为Java语言而开发的,它来源于Strongtalk VM,而这款虚拟机中相当多的技术又是来源于一款支持Self语言实现“达到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编译器与混合的运行时系统

1.4.3 Sun Mobile-Embedded VM/Meta-Circular VM

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-HI VM则是它们的一组参考实现。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)JavaInJava
JavaInJava是Sun公司于1997年~1998年间研发的一个实验室性质的虚拟机,从名字就可以看出,它试图以Java语言来实现Java语言本身的运行环境,既所谓的“元循环”(Meta-Circular,是指使用语言自身来实现其运行环境)。它必须运行在另外一个宿主虚拟机之上,内部没有JIT编译器,代码只能以解释模式执行。在20世纪末主流Java虚拟机都未能很好解决性能问题的时代,开发这种项目,其执行速度可想而知。
(5)Maxine VM
Maxine VM和上面的JavaInJava非常相似,它也是一个几乎全部以Java代码实现(只有用于启动JVM的加载器使用C语言编写)的元循环Java虚拟机。这个项目于2005年开始,到现在仍然在发展之中,比起JavaInJava,Maxine VM就显得“靠谱”很多,它有先进的JIT编译器和垃圾收集器(但没有解释器),可在宿主模式或独立模式下执行,其执行效率已经接近了HotSpot Client VM的水平。

1.4.4 BEA JRockit/IBM J9 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应用。

1.4.5Azul VM/BEA Liquid VM

我们平时所提及的“高性能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公司开始从硬件转向软件,发布了自己的Zing JVM,可以在通用x86平台上提供接近于Vega系统的特性。
Liquid VM即是现在的JRockit VE(Virtual Edition),它是BEA公司开发的,可以直接运行在自家Hypervisor系统上的JRockit VM的虚拟化版本,Liquid VM不需要操作系统的支持,或者说它自己本身实现了一个专用操作系统的必要功能,如文件系统、网络支持等。由虚拟机越过通用操作系统直接控制硬件可以获得很多好处,如在线程调度时,不需要再进行内核态/用户态的切换等,这样可以最大限度地发挥硬件的能力,提升Java程序的执行性能。

Apache Harmony/Google Android Dalvik VM

这节介绍的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一起处于迅猛发展阶段,在Android 2.2中已提供即时编译器实现,在执行性能上有了很大的提高

Microsoft JVM及其他

在十几年的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虚拟机还有(当然,应该还有很多笔者所不知道的):
JamVM.
cacaovm.
SableVM.
Kaffe.
Jelatine JVM.
NanoVM.
MRP.
Moxie JVM.
Jikes RVM.

展望java技术的未来

在2005年,java语言诞生10周年的SunOne技术大会上,java语言之父JamesGosling做了一场题为为“java技术下一个十年”的演讲。。。。。

1.5.1模块化

模块化是解决解决应用系统与技术平台越来越复杂、越来越庞大问题的一个重要途径。无论是开发人员还是产品最终用户,都不希望为了系统中的一小块的功能而不得不下载、安装、部署、及维护整套庞大的系统、站在整个软件工业化的高度来看,模块化是建立各种功能的标准件的前提。最近几年OSGi技术的迅速发展各个厂商在JCP中对模块化规范的激烈斗争,都能充分说明模块化技术的迫切和重要。
在未来的java平台中,很可能会对模块化提出语法层面的支持(java9模块化已出

1.5.2混合语言

当单一的java开发已经无法满足当前软件的复杂需求时,越来越多基于java虚拟机的语言开发被应用到软件项目中,java平台上的多语言混合编程正成为主流,每种语言都可以针对自己擅长的方面更好地解决问题。试想一下,在一个项目中,并行处理用Clojure语言编写,展示层使用JRuby/Rails,中间层使用java,每个应用层都将使用不同的编程语言来完成,而且,接口对每一层的开发者都是透明的,各种语言之间的交互不存在任何困难,就像使用自己语言的原生APi一样方便,因为它们最终运行在java虚拟机之上。
在最近的几年里,Clojure、JRuby、Groovy等新生语言的使用人数不断增长,而运行在java虚拟机之上的语言数量也在迅速膨胀。
除了催生出大量的新语言外,许多已经有了很长历史的程序语言也出现了基于java虚拟机实现的版本,这样使得混合编程对许多以前使用其他语言的“老”程序员也具备相当大的吸引力,软件企业投入了大量资本的现有代码资产也能很好地保护起来。
对这些运行于java虚拟机之上,java之外的语言,来自系统级、底层的支持正在迅速增强,以JSR-292为核心的一系列项目和功能改进,推动java虚拟机从“java语言的虚拟机”向“多语言虚拟机”的方向发展

1.5.3多核并行

如今,CPU硬件的发展方向已经从高频率转变为多核心,随着多核时代的来临,软件开发越来越关注并行编程的领域。早在JDK1.5就已经引入java.util.concurrent包实现了一个粗粒度的并发框架。而JDK1.7中加入的java.util.concurrent.forkjoin包则是对这个框架的一次重要扩充。Fork/Join模式是处理并发编程的一个经典方法。虽然不能解决所有的问题,但是在此模式的适用范围之内,能够轻松地利用多个CPU核心提供的计算资源来协作完成一个复杂的计算任务。通过Fork/Join模式,我们能够更加顺畅地过渡到多核时代
在java8中,将会提供lambda支持,这将会极大改善目前java语言不适合函数式编程的现状,函数式编程的一个重要优点就是这样的程序天然地适合并行运行,这对java语言在多核时代继续保持主流语言的地位有很大帮助。
另外,在并行计算中必须提及的还有OpenJDK的子项目Sumatra,目前显卡的算术运算能力并行能力已经远远超过了CPU,在图形运算领域以外发掘显卡的潜力是最近几年计算机发展的方向之一,例如C语言的CUDA。Sumatra项目就是为java提供使用GPU和APU运算能力的工具,以后它将会直接提供java语言 层面的API,或者为lambda和其他JVM语言提供底层的并行运算支持。
在JDK外围,也出现了专为满足并行运算需求的计算框架,如Apache的Hadoop Map/Readuce,这是一个简单易懂的并行框架,能够运行在由上千个商用机器组成的大型集群上,并且能以一种可靠的容错方式并行处理TB级别的数据集。另外,还出现了诸如Scala、Clojure、及Erlang等天生就具备并行计算能力的语言。

1.5.4进一步丰富语法

java 5 曾经对java语法进行了一次扩充,这次扩充加入了自动装箱、泛型、动态注解、枚举、可变长参数、循环遍历等语法,使得java语言的精确性和易用性有了很大的进步。在java7中,对java语法进行了另一次大规模的扩充。Sun专门为改进java语法在OpenJDK中建立了Coin子项目来统一处理对java语法的细节修改,如二进制的原生支持、在switch语句中支持字符串、"<>"操作符、异常处理的改进、简化变长参数方法调用、面向资源的try-catch-finally语句等都是在Coin项目中提交的内容。
处理Coin项目之外在JSR-335中定义的lambda表达式也将对java的语法和语言习惯产生很大的影响,面向很熟方式的编程可能会成为主流。

1.5.5 64位虚拟机

在几年之前,主流的CPU就开始支持64位架构了。java虚拟机也在很早之前推出了支持64位系统的版本。但java程序运行在64位虚拟机上需要付出的比较大的额外代价:首先是内存问题,由于指针膨胀和各种数据类型对其补白的原因,运行于64位系统上的java应用需要消耗更多的内存,通常要比32位系统额外增加10%~30%的内存消耗;其次,多个几个的测试结果显示,64位虚拟机的运行速度在各项测试中几乎全面落后于32位虚拟机,两者大约有15%左右的性能差距。
但在javaEE方面,企业级应用经常需要使用超过4GB的内存,对于64位虚拟机的需求时非常迫切的,但由于上述原因,许多企业应用仍然选择使用虚拟集群等方式继续在32位虚拟机中进行部署。Sun也注意到了这些问题,并做出了一些改善,在JDK 1.6 Update 14之后,提供了普通对象指针压缩功能(-XX:+UseCompressedOops,这个参数不建议显式设置,建议维持默认由虚拟机的Ergonomics机制自动开启),在执行代码时,动态植入压缩指令以节省内存消耗,但是开启压缩指针会增加执行代码数量,因为所有在Java堆里的、指向Java堆内对象的指针都会被压缩,这些指针的访问就需要更多的代码才可以实现,而且并不只是读写字段才受影响,在实例方法调用、子类型检查等操作中也受影响,因为对象实例指向对象类型的引用也被压缩了。随着硬件的进一步发展,计算机终究会完全过渡到64位的时代,这是一件毫无疑问的事情,主流的虚拟机应用也终究会从32位发展至64位,而虚拟机对64位的支持也将会进一步完善。

1.6实战:自己编译JDK

想要一探JDK内部的实现机制,最便捷的路径之一就是自己编译一套JDK通过阅读和跟踪调试JDK源码去了解java技术体系的原理,虽然门槛会高一点,但肯定会比阅读各种书籍、文章更加贴近本质。另外,JDK中的许多底层方法都是本地化的,需要跟踪这些方法的运作对JDK进行Hack的时候,都需要自己编译一套JDK。
现在网络上有不少开源的JDK实现可以供我们选择,如Apache Harmony、Open-JDK等。考虑到Sun系列的JDK是现在使用得最广泛的JDK版本,笔者选择了Open-JDK进行这次编译实战。

获取JDK源码

首先要明确OpenJDK和Sun/OracleJDK之间,以及OpenJDK 6、Open JDK 7、Open JDK 7u、Open JDK 8等项目之间是什么关系,这有助于确定接下来编译要使用的JDK版本和源码分支。
从前面介绍的java发展史中我们了解到OpenJDK是Sun在2006年末把java开源而形成的项目,这里的“开源”是通常意义上的源码开发形式,即源码是可被复用的例如IcedTea、UltraViolet都是从OpenJDK源码衍生出的发行版。但如果仅从“开源”字面意义(开放可阅读的源码)上看,其实Sun自JDK 1.5之后就开始以Java Research License(JRL)的形式公布过Java源码,主要用于研究人员阅读(JRL许可证的开放源码至JDK 1.6 Update 23为止)。把这些JRL许可证形式的Sun/OracleJDK源码和对应版本的OpenJDK源码进行比较,发现除了文件头的版权注释之外,其余代码基本上都是相同的,只有字体渲染部分存在一点差异,Oracle JDK采用了商业实现,而OpenJDK使用的是开源的FreeType。当然,“相同”是建立在两者共有的组件基础上的,Oracle JDK中还会存在一些Open JDK没有的、商用闭源的功能,例如从JRockit移植改造而来的Java Flight Recorder。预计以后JRockit的MissionControl移植到HotSpot之后,也会以Oracle JDK专有、闭源的形式提供。
再来看一下OpenJDK 6、OpenJDK 7、OpenJDK 7u和OpenJDK 8这几个项目之间的关系,从图1-7(依然是从Joe Darcy的OSCON 2011演示稿中截取的图片)来看,OpenJDK 7是始于JDK 6时期,当时JDK 6和JDK 6 Update 1已经发布,JDK 7已经开始研发了,所以OpenJDK 7是直接基于正在研发的JDK 7源码建立的。但考虑到OpenJDK 7的状况在当时还不适合实际生产部署,因此在OpenJDK 7 Build 20的基础上建立了OpenJDK 6分支,剥离掉JDK 7新功能的代码,形成一个可以通过TCK 6测试的独立分支。
2012年7月,JDK 7正式发布,在OpenJDK中也同步建立了OpenJDK 7 Update项目对JDK 7进行更新升级,以及OpenJDK 8项目开始下一个JDK大版本的研发。按照开发习惯,新的功能或Bug修复通常是在最新分支上进行的,当功能或修复在最新分支上稳定之后会同步到其他老版本的维护分支上。
OpenJDK 6、OpenJDK 7、OpenJDK 7u和OpenJDK 8的源码都可以在它们相应的网页上找到,在本次编译实践中,笔者选用的项目是OpenJDK 7u,版本为7u6。
获取OpenJDK源码有两种方式,其中一种是通过Mercurial代码版本管理工具从Repository中直接取得源码(Repository地址:http://hg.openjdk.java.net/jdk7u/jdk7u),获取过程如以下代码所示。
这是最直接的方式,从版本管理中看变更轨迹比看Release Note效果更好。但不足之处是速度太慢,虽然代码总容量只有300 MB左右,但是文件数量太多,在笔者的网络下全部复制到本地需要数小时。另外,考虑到Mercurial不如Git、SVN、ClearCase或CVS之类的版本控制工具那样普及,对于一般读者,建议采用第二种方式,即直接下载官方打包好的源码包,读者可以从Source Bundle Releases页面(地址:http://jdk7.java.net/source.html)取得打包好的源码,到本地直接解压即可。一般来说,源码包大概一至两个月左右会更新一次,虽然不够及时,但比起从Mercurial复制代码的确方便和快捷许多。笔者下载的是OpenJDK 7 Update 6 Build b21版源码包,2012年8月28日发布,大概99MB,解压后约为339MB。

。。。。。。省略5000字啥时候能有信息编译出来再说

posted @ 2018-12-08 20:57  莫逸风  阅读(72)  评论(0编辑  收藏  举报