Java> 深入理解Java虚拟机(2th)读书笔记 - 第一部分 走近Java

1 走近Java

1.1 概述

Java特点:

  1. 结构严谨、面向对象;
  2. “一次编写,到处运行”(跨平台);
  3. 相对安全的内存管理和访问机制,避免绝大部分内存泄漏和指针越界问题;
  4. 实现热点代码检测和运行时编译及优化;
  5. 有一套完善的应用程序接口,无数来自商业机构和开源社区的第三方类库帮助实现各种功能;

1.2 Java技术体系

Sun公司定义的Java技术规范体系组成部分:

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

JDK(Java Development Kit)是指:Java程序设计语言、Java虚拟机、Java API类库这三部分,用于支持Java程序开发的最小环境。
JRE(Java Runtime Environment):Java API类库中的Java SE API子集和Java虚拟机。

Java技术体系如果按所服务领域来划分,分成4个平台:

  • Java Card: 支持Java小程序(Applets)运行在小内存设备(如智能卡)上的平台;
  • Java ME(Micro Edition):支持Java程序运行在移动终端(手机、PDA)平台。Java API精简版本,支持移动端,以前称为J2ME。
  • Java SE(Standard Edition):支持面向桌面级应用,提供完整的Java核心API,以前成J2SE。
  • Java EE(Enterprise Edition):支持使用多层架构的企业应用(ERP,CRM应用),= Java SE API + 扩充,以前称为J2EE。

1.3 Java发展史

1991年4月,James Gosling博士开始启动,Oak(橡树)是Java前身。
1995年5月,Oak改名Java,发布Java 1.0,第一次提出"Write Once, Run Anywhere"。
1996年1月,JDK 1.0发布,Java第一个正式版本运行环境(Sun Classic VM)。JDK 1.0包括:Java虚拟机、Applet、AWT等。
1996年4月,10个OS供应商声明将在产品中嵌入Java。
1996年5月,Sun于旧金山举行首届JavaOne大会,自此JavaOne成每年一度Java技术盛会。
1997年2月,JDK 1.1,包括:JAR文件格式、JDBC、JavaBeans、RMI。Java语法:内部类、反射等。
1999年4月,JDK 1.1.4之后,每个JDK版本都有一个名字。
1998年12月,JDK 1.2,Java技术体系分3个方向:J2SE、J2EE、J2ME。
1999年4月,HotSpot虚拟机发部,97年被Sun收购,成为JDK 1.3及以后所有版本Sun JDK默认虚拟机。
2000年5月,JDK 1.3,改进类库:数学运算和新Timer API,JNDI服务作为JDK平台级服务,使用CORBA IIOP来实现RMI通信协议等。对Java 2D改进,添加JavaSound类库。JDK 1.3以后,Sun维持2年发布一个JDK主要版本习惯,以动物命名。
2002年2月,JDK 1.4 开始成熟版本,新技术特性:正则表达式、异常链、NIO、日志类、XML解析器、XSLT转换器等。
2002年,微软发布.NET Framework与Java诸多相近,作为Java主要竞争对手。
2004年9月,JDK 1.5发布(Tiger),语法易用性做了大改进:自动装箱、泛型、动态注解、枚举、可变长参数、foreach遍历循环等语法特性。内存模型改进(Java Memory Model , JMM)、提供java.util.concurrent并发包等,最后一个支持Windows 9x的JDK版本。
2006年12月,JDK 1.6发布(Mustang),终结J2EE、J2ME、J2SE命名方式,启用Java SE 6、Java ME6、Java EE 6的命名方式。改进:提供动态语言支持(内置Mozilla JavaScript Rhinio引擎实现)、提供编译API和微型HTTP服务器API等)。Java虚拟机改进:锁与同步、垃圾收集、类加载等算法改动。
2006年11月,Java开源,GPL v2协议开源。OpenJDK 7与JDK 1.7代码除头文件注释,其余完全相同。
2009年2月,JDK 1.7(Dolphin)第一个版本发布,由于各种原因后续波拿巴无法按计划完成。
2009年4月,Oracle收购Sun公司,Java商标归Oracle,Java语言不属于哪家公司由JCP组织管理。收购后,Oracle实行“B”计划,大部分裁剪JDK 1.7目标,确保JDK 1.7能于2011年7月发布,其余延迟到JDK 1.8。最终,JDK 1.7改进:提供新G1收集器、加器对非Java语言的调用支持、升级类加载架构等。
2013年9月,JDK 1.8发布,新特性:Lambda表达式等。
2011年,Oracle提到JDK 1.9 规划。

1.4 Java 虚拟机发展史

1.4.1 Sun Classic/Exact VM

世界上第一款商用Java虚拟机,目前已经终结。只能使用纯解释器方式来执行Java代码,如果要使用JIT编译器,必须外挂。后来Sun虚拟机团队发布Exact VM虚拟机,解决Classic VM所面临各种问题。虽然Exact VM相对Classic VM改进许多,不过很快就被HotSpot VM所取代。JDK 1.4 Classic VM完全退出历史舞台,HotSpot VM作为默认虚拟机。

1.4.2 Sun HotSpot VM

Sun JDK和OpenJDK中多带的虚拟机,不过是由一家名为Longview Technologies小公司设计的,后来被Sun收购。2008年和2009年,Oracle收购BEA和Sun,分别拥有JRockit VM和HotSpot VM,计划在JDK 8时完成2个虚拟机合并。

1.4.3 Sun Mobile-Embedded VM / Meta-Cicular VM

针对移动和嵌入式设备:

  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是一组参考实现。

  3. Squawk VM
    Sun公司开发,运行于一种手持Wifi设备Sun SPOT,也曾用于Java Card。

  4. JavaInJava
    Sun 97~98年研发的实验室性质虚拟机,试图以Java实现Java本身运行环境,所谓“元循环”(Meta-Circular,指使用语言本身实现运行环境)。不过,为你解决性能问题。

  5. Maxine VM
    与JavaInJava相似,几乎全部以Java代码实现(只有用于启动JVM的加载器使用C)。05年开始,现在仍在发展中,有先进JIT编译器和垃圾收集器(没有解释器),可在宿主模式或独立模式下执行,效率接近HotSpot Client VM。

1.4.4 BEA JRockit / IBM J9 VM

Sun公司以外规模最大影响、最著名的2款虚拟机。
JRockit VM曾经号称“世界上速度最快的Java虚拟机”,是BEA公司2002年从Appcal Virtual Machines公司收购的虚拟机,BEA公司发展成一款专门为服务器硬件和服务器应用场景高度优化的虚拟机。

IBM J9 VM是IBM主力发展的Java 虚拟机,由IBM Ottawa实验室一个名为SmallTalk的虚拟机扩展而来。IBM J9 市场定位与Sun HotSpot比较接近,是一款设计上从服务器到桌面应用再到嵌入式都全面考虑的多用途虚拟机,J9主要市场是和IBM产品(IBM WebSphere等)搭配以及IBM AJX和z/OS平台上部署Java应用。

1.4.5 Azul VM / BEA Liquid VM

Azul VM是Azul Systems公司在HotSpot基础上进行大量改进,运行于Azul Systems公司专有硬件Vega系统上的Java虚拟机,每个Azul VM实例都可以管理至少数十个CPU和数百GB内存硬件资源,并提供巨大内存范围内实现可控的GC时间垃圾收集器,为专有硬件优化的线程调度等。2010年,Azul Systems开始从硬件转向软件,发布Zing VM,在通用x86平台上接近Vega系统特性。

Liquid VM即现在的JRockit VE(Virtual Edition),由BEA公司开发,不需要操作系统支持(本身实现了一个专用操作系统)。

1.4.6 Apache Harmony / Google Andriod Dalvik VM

Apache Harmony是一个Apache软件基金会以Apache License协议开源的、兼容JDK 1.5/1.6的Java程序运行平台,但没有通过TCK认证,不能称为“Apache的JDK”。后来Apache退出JCP组织,Java社区出现严重一次“分裂”。虽然Apache Harmnony没有大规模商用,但是其代码(Java库部分的代码)被吸纳进IBM JDK 7实现,以及Google Android SDK中,对Andriod发展起到很大作用。

Dalvik VM是Andriod核心组成部分之一,名字源于冰岛一个名为Dalvik的小渔村。Dalvik VM并不是Java虚拟机,因为没有遵循Java虚拟机规范,不能直接执行Java的Class文件,使用的是寄存器架构,而不是JVM中常见栈架构。但是与Java又有些联系,执行的dex(Dalvik Executable)文件可用通过Class文件转化而来,使用Java语法编写应用程序,能直接使用大部分Java API。Andriod 2.2 已提供及时编译器实现,在执行性能上有很大提高。

1.4.7 Microsoft JVM及其他

微软的主要应用之一是在浏览器中运行Java Appletes程序,1996~1997年,为在IE3支持Java Applets应用而开发Java虚拟机,不过只有Windows平台版本,却是当时Windows下性能最好的Java虚拟机。不过,由于与Sun公司争夺Java控制权,Microsoft最终放弃了开发VM支持Java。

1.5 展望Java未来

1.5.1 模块化

接近应用系统与技术平台越来越复杂、越来越庞大问题的一个重要途径。

1.5.2 混合语言

每种语言都有自己擅长的方面,多语言混合编程能发挥除语言各自的特点。不同语言运行在一个虚拟机之上,开发者只需关心接口,而不关心具体实现。

1.5.3 多核并行

时代背景:CPU硬件发展方向已经从高频率转变为多核心,软件领域也越来越关注并行编程。
JDK 1.5引入java.util.concurrent包实现粗粒度的并发框架;
JDK 1.7加入java.util.concurrent.forjoin包是重要扩展,Fork/Join模式是处理并行编程的一个经典方法,能轻松利用多个CPU核心提供的计算资源来协作完成一个复杂的计算任务。

Java 8提供Lambda支持,极大改善目前Java不适合函数式编程的现状。
目前显卡的算术运算能力、并行能力已远超CPU,OpenJDK的子项目Sumatra,就是为Java提供使用GPU(Graphics Processing Units)和APU(Accelerated Processing Units )运算能力的工具,以后还会直接提供Java API/Lambda,提供底层的并行运算支持。

JDK的外围,也出现专门为满足并行计算需求的计算框架,如Apache的Hadoop Map/ Reduce,简单易懂。另外,还有Scala, Clojure, Erlang等天生具备并行计算能力的语言。

1.5.4 进一步的丰富语法

Java5 扩充:自动装箱、泛型、动态注解、枚举、可变长参数、遍历循环等语法,改善了语言精确性和易用性。
Java7/8扩充:专门为改进Java语法在OpenJDK中建立Coin子项目,来统一处理对Java语法细节修改,如二进制数的原生支持,switch中支持字符串,"<>"操作符、异常处理的改进、简化边长参数方法调用、面向资源的try-catch-finally语句等。JSR-335定义Lambda表达式。

1.5.5 64位虚拟机

Java虚拟机很早退出64位系统的版本,不过APP运行在64位虚拟机上,需要付出比较大的额外代价:
1)内存问题,指针膨胀和各种数据类型对齐补白的原因,需要比32位系统消耗更多内存,约10%~30%;
2)64位虚拟机运行速度各个测试项几乎全面落后于32位虚拟机,有15%左右性能差距;

1.6 实战:自己编译JDK

可选择开源JDK版本比较多,如Apache Harmony, OpenJDK等,本文使用OpenJDK。

1.6.1 获取JDK源码

Sun JDK 1.5之后,OpenJDK 6, OpenJDK 7基本相当于Oracle JDK 6, Oracle JDK 7。区别在于许可证不一样,OpenJDK 使用开源FreeType,供研究人员阅读;Oracle JDK采用商业实现。另外,Oracle JDK中还存在一些OpenJDK没有的、商业闭源功能,如从JRockit移植而来的Java Flight Recorder。

以获取OpenJDK 7u(版本:7u6)为例,获取OpenJDK源码方式:

  1. 通过Mercurial代码版本关联工具从Repository中直接取得(地址:http://hg.openjdk.java.net/jdk7u/jdk7u)
    获取代码:
hg clone http://hg.openjdk.java.net/jdk7u/jdk7u-dev
cd jdk7u-dev
chmod 755 get_source.sh
./get_source.sh

优点:从版本关联看变更轨迹比Release Note效果更好;
缺点:速度太慢,总容量300MB,不过文件数量太多,复制到本地需要数小时。

  1. 采用Git/SVN/ClearCase或者CVS等版本控制工具,直接下载官方打包好的源码包,到本地解压
    如Source Bundle Releases页面:http://jdk7.java.net/source.html

推荐使用Linux, MacOS或Solaries上构建OpenJDK,阅读源码中的README-builds.html文档。

注意事项:

  • 编译64位OpenJDK,需要采用64位操作系统;
  • 5GB以上空余磁盘空间;
  • 不要放在中文目录下;

1.6.3 构建编译环境

MacOS,安装最新版XCode和Command Line Tools for XCode + 6u14以上版本JDK(部分OpenJDK是Java编写);
Linux类似于MacOS,不过不需要按照Xcode提供gcc,因为Unbuntu默认按照好gcc(4.3以上版本,安装binutils即可)。
Unbuntu 10.10下编译OpenJDK 7u4所需依赖可使用命令一次完成:

sudo apt-get install build-essential gawk m4 openjdk-6-jdk
libasound2-dev libcups2-dev libxrender-dev xorg-dev xutils-dev
xllproto-print-dev binutils libmotif3 libmotif-dev ant

1.6.4 进行编译

必须设置的环境变量LANG和ALT_BOOTDIR,前者设定语言选项,必须设置为 export LANG=C,否则编译会出现一个HashTable内的空指针异常。
ALT_BOOTDIR参数是Bootstrap JDK,在MacOS上设置为JDK honme路径。
如果之前设置了JAVA_HOME和CLASSPATH两个环境变量,编译前必须取消,否则Makefile脚本检查到有这2个变量存在会警告。

unset JAVA_HOME
unset CLASSPATH

详细命令行步骤:略

1.6.5 在IDE中进行源码调试

资深开发人员可能采用GDB+VIM开发HotSpot,不过这里介绍IDE环境下如何调试。

  1. 下载、安装NetBeans
    NetBeans官网下载最新版NetBeans,选择支持C/C++开发的版本。

  2. 创建源代码项目
    安装后,新建项目“基于现有源代码的C/C++项目”,在源码文件夹中填入OpenJDK目录下的hotspot目录的路径,在单选按钮中选择“定制”。然后,在“指定构建代码的方法”中选择“使用现有的makefile”,并填入Makefile文件的路径(hotspot/make目录下),并将“构建命令”修改为:

${MAKE} -f Makefile clean jvmg
ALT_BOOTDIR = /Library/Java/JavaVirtualMachines/jdk1.7.0_04.jdk/Contents/Home
ARCH_DATA_MODEL=64 LANG=C

注意:路径要根据自己安装JDK版本,以及安装路径进行选择

  1. 设置编译结果路径
    此时HotSpot还无法运行,因为NetBeans不知道编译出来的结果放在哪里,哪个程序是虚拟机入口等,需要明确告诉NetBeans。方法是HotSpot工程单击右键,在弹出快捷菜单中选择“属性”, 在弹出的对话框中找到“运行”选项,设置运行命令:
/Users/lcyFnix/Develop/JVM/jdkBuild/openjdk_7u4/hotspot/build/bsd/bsd_amd64_compiler2/jvmg/gmma Queens

Queens是Makefile脚本自动产生的一段八皇后问题的Java程序,用于测试虚拟机。完全可以替换为自己的Java程序。

一般情况下,难以直接跟踪Java代码如何在虚拟机中执行,因为HotSpot在主流OS都采用模板解释器来执行字节码,与JIT编译器一样,最终执行的汇编代码都是运行期间产生,无法直接设置断点。HotSpot增加了参数方便开发人员调试解释器:
-XX:+TraceBytecodes -XX:StopInterpreterAt=<n>

参数作用是当遇到序号未的字节码指令时,便会中断程序执行,进入断点调试,在调试解释器部分代码时,把这两个参数加到gamma后面即可。

  1. 设置环境变量
    需要设置LD_LIBRARY_PATH, JAVA_HOME, CLASSPATH这3个环境变量。
  • HotSpot源码结构
-hotspot
  |--agent                                         Serviceability Agent的实现
  |--make                                          用来build出HotSpot的各种配置文件
  |--src                                           HotSpot VM的源代码
  |    |--cpu                                      CPU相关代码
  |    |--os                                       操作系统相关代码
  |    |--os cpu                                   操作系统+cpu组合的相关代码
  |    |--share                                    平台无关的共通代码
  |        |--fools                                工具..
  |        |    |--hsdis                           反汇编插件
  |        |    |--IdealGraphVisualizer            将Server 编译器的中间代码可视化的工具
  |        |    |--launcher                        启动程序“java”
  |        |    |--LogCompilation                  将-XX:LogCompilation输出的日志(hotspot.log)整理或更容易阅读的格式的工具
  |        |    |--ProjectCreator                  生成Visual Studio的project文件的工具
  |        |--vm                                   HotSpot Vm的核心代码
  |             |--adlc                            平台描述文件(上面的cpu或os_cpu里的*.ad文件)的编译器

  |             |--asm                             汇编器接口
  |             |-cl                               C1Fent编译器
  |             |--ci                              动态编译器的公共服务接口
  |             |--classHle                        类文进的处理(包括类加载和系统符号表等)
  |             |--code                            动态生成的代码的管理
  |             |--compiler                        编译器接口
  |             |--gc_implementation               GC的实现
  |             |    |--concurrentMarkSweep        Concurrent Mark Sweep GC的实现
  |             |    |--gf                         Garbage-First GC的实现(不使用老的分代式GC框架)

  |             |    |--parallelScavenge           ParallelScavenge GC的实现(Server VM默认,不使用老的分代式GC框架)

  |             |    |--parNev                     ParNev GC的实现
  |             |    |--shared                     GC的共通实现
  |             |--gc_interface                    GC的接口
  |             |--interpreter                     解释器,包括“模板解释器”(官方板在用)和“C++解释器”(官方版不再用)
  |             |--libadt                          一些抽象数据结构
  |             |--memory                          内存管理相关(包含老的分代式GC框架)
  |             |--oops                            HotSpot VM的对象系统实现
  |             |--opto                            Server编译器
  |             |--prims                           HotSpot VM的对外接口,包括部分标准库的native部分和JVMTI实现

  |             |--runtime                         运行时支持库(包括线程管理、编译器调度、锁、反射等)

  |             |--services                        主要是用例支持JMX之类的管理功能的接口
  |             |--shark                           基于LLVM的JIT编译器(官方版没有使用)
  |             |--utilities                       一些基本的工具类
  |--test                                          单元测试

1.7 本章小结

介绍了Java技术体系的历史、发展,通过实战介绍如何独立编译一个OpenJDK 7.后面章节分4个部分介绍Java内存管理、Class文件结构与执行引擎、编译器优化及多线程并发方面的实现原理。

重要知识点

  1. Java技术体系组成 (技术划分,服务领域划分);
  2. “一次编写,到处运行” 具体体现;
  3. Java虚拟机,与其他重要虚拟机如Dalvik关系;
  4. Java虚拟机源码如何编译(OpenJDK);
posted @ 2021-01-06 20:09  明明1109  阅读(273)  评论(0编辑  收藏  举报