第1章 走进 Java
Java 技术体系
从广义上讲,Clojure、JRuby、Groovy等运行于Java虚拟机上的语言及其相关的程序都
属于Java技术体系中的一员。如果仅从传统意义上来看,Sun官方所定义的Java技术体系包括以下几个组成部分:
- Java程序设计语言
- 各种硬件平台上的Java虚拟机
- Class文件格式
- Java API类库
- 来自商业机构和开源社区的第三方Java类库
JDK:Java程序设计语言 + Java虚拟机 + Java API 类库 = 支持Java程序开发的最小环境。
JRE:Java API 类库的子集 Java SE API + Java 虚拟机 = 支持Java程序运行的标准环境。
Java技术体系可以分为4个平台,分别为:
- Java Card:支持一些Java小程序(Applets)运行在小内存设备(如智能卡)上的平台。
- Java ME(Micro Edition):支持Java程序运行在移动终端(手机、PDA)上的平台,对Java API有所精简,并加入了针对移动终端的支持,这个版本以前称为J2ME。Android 并不属于 Java ME。
- Java SE(Standard Edition):支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API,这条产品线在JDK 6以前被称为J2SE。
- Java EE(Enterprise Edition):支持使用多层架构的企业应用(如ERP、MIS、CRM应用)的Java平台,除了提供Java SE API外,还对其做了大量有针对性的扩充,并提供了相关的部署支持,这条产品线在JDK 6以前被称为J2EE,在JDK 10以后被Oracle放弃,捐献给Eclipse基金会管理,此后被称为Jakarta EE。
Java 发展史
- 1991.04 Green Porject 启动,产物:Oak
- 1995.05 Oak 改名 Java 第一次提出“Write Once,Run Anywhere”的口号
- 1961.01 JDK 1.0 Java虚拟机、Applet、AWT等
- 1996.05 Sun于美国旧金山举行了首届JavaOne大会
- 1997.02 JDK 1.1 代表技术:JAR文件格式、JDBC、JavaBeans、RMI、内部类、反射等
- 1998.12 JDK 1.2 EJB、Java Plug-in、Java IDL、Swing等,并且第一次内置了JIT(Just In Time)即时编译器,添加类一系列Collections集合类等
- 1999.04 HotSpot 虚拟机诞生。Hot-Spot虚拟机刚发布时是作为JDK 1.2的附加程序提供的,后来它成为JDK 1.3及之后所有JDK版本的默认Java虚拟机
- 2000.05 JDK 1.3 Java类库的改进
- 2002.02 JDK 1.4 正则表达式,异常链,NIO,日志类,XML解析器等
- 2004.09 JDK 5 自动装箱,泛型,动态注解,枚举,foreach,并发包,改进了Java的内存模型
- 2006.11 Sun公司宣布计划把Java开源 陆续地将JDK的各个部分在GPL v2协议下公开了源码,并建立了OpenJDK组织对这些源码进行独立管理
- 2006.12 JDK 6 提供初步的动态语言支持、提供编译期注解处理器和微型HTTP服务器API,对Java虚拟机内部做了大量改进,包括锁与同步、垃圾收集、类加载等方面的实现
- 2009.02 JDK 7 提供新的G1收集器、加强对非Java语言的调用支持、可并行的类加载架构等
- 2009.04 Oracle宣布正式以74亿美元的价格收购市值曾超过2000亿美元的Sun公司,传奇的Sun Microsystems从此落幕成为历史,Java商标正式划归Oracle所有
- 2014.03 JDK 8 对Lambda表达式的支持,内置Nashorn,JavaScript引擎的支持,新的时间日期API,彻底移除HotSpot的永久代等
- 2017.09 JDK 9 Jigsaw,增强了若干工具(JS Shell、JLink、JHSDB等),整顿了HotSpot各个模块各自为战的日志系统,支持HTTP 2客户单API等91个JEP 以后JDK将会在每年的3月和9月各发布一个大版本,每六个大版本中会被划出一个长期支持(Long Term Support,LTS)版,拥有为期三年的支持和更新,普通版的JDK就只有短短六个月的生命周期
- 2018.03 JDK 10 内部重构,诸如统一源仓库、统一垃圾收集器接口、统一即时编译器接口等
- 2018.03 Oracle正式宣告Java EE成为历史名词,所有权直接赠送给Eclipse基金会,唯一的条件是以后不准再使用“Java”这个商标,所以取而代之的将是Jakarta EE。
- 2018.10,JavaOne 2018在旧金山举行,这也是最后一届JavaOne大会,随后被Oracle裁撤,此外还有Java Mission Control的开发团队,也在2018年6月被Oracle解散。
- 2018.09 JDK 11 LTS Oracle 调整了JDK的授权许可证。首先,Oracle从JDK 11起把以前的商业特性全部开源给OpenJDK,这样OpenJDK 11和OracleJDK 11的代码和功能,在本质上就是完全相同的。然后,Oracle宣布以后将会同时发行两个JDK:一个是以GPLv2+CE协议下由Oracle发行的OpenJDK,另一个是在新的OTN协议下发行的传统的OracleJDK,这两个JDK共享绝大部分源码,在功能上是几乎一样的,核心差异是前者可以免费在开发、测试或生产环境中使用,但是只有半年时间的更新支持;后者个人依然可以免费使用,但若在生产环境中商用就必须付费,可以有三年时间的更新支持。
- 2019.02 Oracle放弃对上一个版本OpenJDK的维护,RedHat同时从Oracle手上接过OpenJDK 8和OpenJDK 11的管理权利和维护职责。
- 2019.03 JDK 12 加入了由RedHat领导开发的Shen-andoah垃圾收集器
- 2019.09 JDK 13 switch表达式改进,文本块等
Java 虚拟机
-
Sun Classic VM
JDK 1.0,Java语言首次拥有了商用的正式运行环境,这个JDK中所带的虚拟机就是Classic VM。这款虚拟机只能使用纯解释器方式来执行Java代码,如果要使用即时编译器那就必须进行外挂,但是假如外挂了即时编译器的话,即时编译器就会完全接管虚拟机的执行系统,解释器便不能再工作了。这个阶段的虚拟机虽然用了即时编译器输出本地代码,其执行效率也和传统的C/C++程序有很大差距,“Java语言很慢”的印象就是在这阶段开始在用户心中树立起来的。 -
Exact VM
在JDK 1.2时,Sun曾在Solaris平台上发布过一款名为Exact VM的虚拟机,它的编译执行系统已经具备现代高性能虚拟机雏形,如热点探测、两级即时编译器、编译器与解释器混合工作模式等。Exact VM因使用准确式内存管理,即是指虚拟机可以知道内存中某个位置的数据具体是什么类型。 -
HotSpot VM
它是Sun/OracleJDK和OpenJDK中的默认Java虚拟机,也是目前使用范围最广的Java虚拟机。但它最初并非由Sun开发,甚至并非是为Java语言开发。而是由Longwiew Technology这家公司开发,源于Strongtalk虚拟机。Sun注意到这款虚拟机在即时编译等多个方面有着优秀的理念和实际成果,在1997年收购了该公司,从而获得了HotSpot虚拟机。
HotSpot虚拟机的热点代码探测能力可以通过执行计数器找出最具有编译价值的代码,然后通知即时编译器以方法为单位进行编译。如果一个方法被频繁调用,或方法中有效循环次数很多,将会分别触发标准即时编译和栈上替换编译(On-Stack Replacement,OSR)行为。通过编译器与解释器恰当地协同工作,可以在最优化的程序响应时间与最佳执行性能中取得平衡,而且无须等待本地代码输出才能执行程序,即时编译的时间压力也相对减小,这样有助于引入更复杂的代码优化技术,输出质量更高的本地代码。 -
Mobile/Embedded VM
移动和嵌入式方向上的Java虚拟机。Oracle公司在Java ME这条产品线上的虚拟机名为CDC-HI(C Virtual Machine,CVM)和CLDC-HI(Monty VM)。 -
BEA JRockit VM
BEA System公司的JRockit是一款专门为服务器硬件和服务端应用场景高度优化的虚拟机,由于专注于服务端应用,它可以不太关注于程序启动速度,因此JRockit内部不包含解释器实现,全部代码都靠即时编译器编译后执行。随着BEA被Oracle收购,现已不再继续发展,永远停留在R28版本,这是JDK 6版JRockit的代号。 -
IBM J9 VM
J9虚拟机最初是由IBM Ottawa实验室的一个SmallTalk虚拟机项目扩展而来。IBM J9虚拟机的市场定位与HotSpot比较接近,它是一款在设计上全面考虑服务端、桌面应用,再到嵌入式的多用途虚拟机。IBM J9虚拟机的职责分离与模块化做得比HotSpot更优秀,由J9虚拟机中抽象封装出来的核心组件库(包括垃圾收集器、即时编译器、诊断监控子系统等)就单独构成了IBM OMR项目,可以在其他语言平台如Ruby、Python中快速组装成相应的功能。从2016年起,IBM逐步将OMR项目和J9虚拟机进行开源,完全开源后便将它们捐献给了Eclipse基金会管理,并重新命名为Eclipse OMR和OpenJ9。 -
BEA Liquid VM
Liquid VM也被称为JRockit VE,它是BEA公司开发的可以直接运行在自家Hypervisor系统上的JRockit虚拟机的虚拟化版本,Liquid VM不需要操作系统的支持,或者说它自己本身实现了一个专用操作系统的必要功能,如线程调度、文件系统、网络支持等。这样可以最大限度地发挥硬件的能力,提升Java程序的执行性能。随着JRockit虚拟机终止开发,Liquid VM项目也已经停止了。 -
Azul VM
Azul VM是Azul Systems公司在HotSpot基础上进行大量改进,运行于Azul Systems公司的专有硬件Vega系统上的Java虚拟机,每个Azul VM实例都可以管理至少数十个CPU和数百GB的内存的硬件资源,并提供在巨大内存范围内停顿时间可控的垃圾收集器(即业内赫赫有名的PGC和C4收集器),为专有硬件优化的线程调度等优秀特性。2010年起,Azul公司的重心逐渐开始从硬件转向软件,发布了自己的Zing虚拟机,可以在通用x86平台上提供接近于Vega系统的性能和一致的功能特性。Azul Systems公司最终放弃了Vega产品线,把全部精力投入到Zing和Zulu产品线中。 -
Zing VM
Zing虚拟机是一个从HotSpot某旧版代码分支基础上独立出来重新开发的高性能Java虚拟机,它可以运行在通用的Linux/x86-64平台上。Azul公司为它编写了新的垃圾收集器,也修改了HotSpot内的许多实现细节,在要求低延迟、快速预热等场景中,Zing VM都要比HotSpot表现得更好。Zing的PGC、C4收集器可以轻易支持TB级别的Java堆内存,而且保证暂停时间仍然可以维持在不超过10毫秒的范围里,HotSpot要一直到JDK 11和JDK 12的ZGC及Shenandoah收集器才达到了相同的目标,而且目前效果仍然远不如C4。Zing的ReadyNow!功能可以利用之前运行时收集到的性能监控数据,引导虚拟机在启动后快速达到稳定的高性能水平,减少启动后从解释执行到即时编译的等待时间。Zing自带的ZVision/ZVRobot功能可以方便用户监控Java虚拟机的运行状态,从找出代码热点到对象分配监控、锁竞争监控等。Zing能让普通用户无须了解垃圾收集等底层调优,就可以使得Java应用享有低延迟、快速预热、易于监控的功能,这是Zing的核心价值和卖点,很多Java应用都可以通过长期努力在应用、框架层面优化来提升性能,但使用Zing的话就可以把精力更多集中在业务方面。
编译 JDK
在 Ubuntu 18.04 LTS 上编译 OpenJDK12:
- 构建编译环境
sudo apt-get install build-essential
- 安装依赖
sudo apt-get install libcups2-dev
sudo apt-get install libx11-dev libxext-dev libxrender-dev libxrandr-dev libxtst-dev libxt-dev
sudo apt-get install libasound2-dev
sudo apt-get install libfontconfig1-dev
sudo apt-get install autoconf
- 安装 Bootstrap JDK
假设要编译大版本号为N的JDK,我们还要另外准备一个大版本号至少为N-1的、已经编译好的JDK,这是因为OpenJDK由多个部分(HotSpot、JDK类库、JAXWS、JAXP……)构成,其中一部分(HotSpot)代码使用C、C++编写,而更多的代码则是使用Java语言来实现,因此编译这些Java代码就需要用到另一个编译期可用的JDK,官方称这个JDK为“Bootstrap JDK”。
sudo apt-get install openjdk-11-jdk
- 编译
编译参数:./configure --help
- --with-debug-level=
:设置编译的级别,可选值为release、fastdebug、slowdebug,越往后进行的优化措施就越少,带的调试信息就越多。默认值为release。 - --enable-debug:等效于--with-debug-level=fastdebug。
- --with-native-debug-symbols=
:确定调试符号信息的编译方式,可选值为none、internal、external、zipped。 - --with-version-string=
:设置编译JDK的版本号,譬如java-version的输出就会显示该信息。这个参数还有--with-version- = 的形式,其中part可以是pre、opt、build、major、minor、security、patch之一,用于设置版本号的某一个部分。 - --with-jvm-variants=
:编译特定模式(Variants)的HotSpot虚拟机,可以多个模式并存,可选值为server、client、minimal、core、zero、custom。 - --with-jvm-features=
:针对--with-jvm-variants=custom时的自定义虚拟机特性列表(Features),可以多个特性并存,由于可选值较多,请参见help命令输出。 - --with-target-bits=
:指明要编译32位还是64位的Java虚拟机,在64位机器上也可以通过交叉编译生成32位的虚拟机。 - --with-
= :用于指明依赖包的具体路径,通常使用在安装了多个不同版本的Bootstrap JDK和依赖包的情况。其中lib的可选值包括boot-jd、freetype、cups、x、alsa、libffi、jtreg、libjpeg、giflib、libpng、lcms、zlib。 - --with-extra-
= :用于设定C、C++和Java代码编译时的额外编译器参数,其中flagtype可选值为cflags、cxxflags、ldflags,分别代表C、C++和Java代码的参数。 - --with-conf-name=
:指定编译配置名称,OpenJDK支持使用不同的配置进行编译,默认会根据编译的操作系统、指令集架构、调试级别自动生成一个配置名称,譬如“linux-x86_64-server-release”,如果在这些信息都相同的情况下保存不同的编译参数配置,就需要使用这个参数来自定义配置名称。
- --with-debug-level=
./configure
make images
编译完成:Finished building target 'images' in configuration 'linux-x86_64-server-release'
- 文件目录 ./build/.../:
- buildtools/:用于生成,存放编译过程中用到的工具
- hotspot/:HotSpot虚拟机编译的中间文件
- images/:使用make *-image产生的镜像存放在这里
- jdk/:编译后产生的JDK
- support/:编译时产生的中间文件
- test-results/:编译后的自动化测试结果
- configure-support/:configure临时文件
- make-support/:make临时文件
- test-support/:test临时文件
将jdk目录下文件复制到JAVA_HOME目录,即可作为一个完整JDK使用。
参考资料:《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》 周志明 著