描述
开 本: 16开纸 张: 胶版纸包 装: 平装-胶订是否套装: 否国际标准书号ISBN: 9787111421900
本书第1版两年内印刷近10次,4家网上书店的评论近4?000条,98%以上的评论全部为5星级的好评,是整个Java图书领域公认的经典著作和超级畅销书,繁体版在台湾也十分受欢迎。第2版在第1版的基础上做了很大的改进:根据*的JDK 1.7对全书内容进行了全面的升级和补充;增加了大量处理各种常见JVM问题的技巧和*实践;增加了若干与生产环境相结合的实战案例;对第1版中的错误和不足之处的修正;等等。第2版不仅技术更新、内容更丰富,而且实战性更强。
本书第1版两年内印刷近10次,4家网上书店的评论近4?000条,98%以上的评论全部为5星级的好评,是整个Java图书领域公认的经典著作和超级畅销书,繁体版在台湾也十分受欢迎。第2版在第1版的基础上做了很大的改进:根据*的JDK1.7对全书内容进行了全面的升级和补充;增加了大量处理各种常见JVM问题的技巧和*实践;增加了若干与生产环境相结合的实战案例;对第1版中的错误和不足之处的修正;等等。第2版不仅技术更新、内容更丰富,而且实战性更强。
全书共分为五大部分,围绕内存管理、执行子系统、程序编译与优化、高效并发等核心主题对JVM进行了全面而深入的分析,深刻揭示了JVM的工作原理。*部分从宏观的角度介绍了整个Java技术体系、Java和JVM的发展历程、模块化,以及JDK的编译,这对理解本书后面内容有重要帮助。第二部分讲解了JVM的自动内存管理,包括虚拟机内存区域的划分原理以及各种内存溢出异常产生的原因;常见的垃圾收集算法以及垃圾收集器的特点和工作原理;常见虚拟机监控与故障处理工具的原理和使用方法。第三部分分析了虚拟机的执行子系统,包括类文件结构、虚拟机类加载机制、虚拟机字节码执行引擎。第四部分讲解了程序的编译与代码的优化,阐述了泛型、自动装箱拆箱、条件编译等语法糖的原理;讲解了虚拟机的热点探测方法、HotSpot的即时编译器、编译触发条件,以及如何从虚拟机外部观察和分析JIT编译的数据和结果;第五部分探讨了Java实现高效并发的原理,包括JVM内存模型的结构和操作;原子性、可见性和有序性在Java内存模型中的体现;先行发生原则的规则和使用;线程在Java语言中的实现原理;虚拟机实现高效并发所做的一系列锁优化措施。
前言
第一部分 走近Java
第1章 走近Java
1.1 概述
1.2 Java技术体系
1.3 Java发展史
1.4 Java虚拟机发展史
1.4.1 Sun Classic Exact VM
1.4.2 Sun HotSpot VM
1.4.3 Sun Mobile-Embedded VM Meta-Circular VM
1.4.4 BEA JRockit IBM J9 VM
1.4.5 Azul VM BEA Liquid VM
1.4.6 Apache Harmony Google Android Dalvik VM
1.4.7 Microsoft JVM及其他
1.5 展望Java技术的未来
1.5.1 模块化
1.5.2 混合语言
1.5.3 多核并行
1.5.4 进一步丰富语法
1.5.5 64位虚拟机
1.6 实战:自己编译JDK
1.6.1 获取JDK源码
1.6.2 系统需求
1.6.3 构建编译环境
1.6.4 进行编译
1.6.5 在IDE工具中进行源码调试
1.7 本章小结
第二部分 自动内存管理机制
第2章 Java内存区域与内存溢出异常
2.1 概述
2.2 运行时数据区域
2.2.1 程序计数器
2.2.2 Java虚拟机栈
2.2.3 本地方法栈
2.2.4 Java堆
2.2.5 方法区
2.2.6 运行时常量池
2.2.7 直接内存
2.3 HotSpot虚拟机对象探秘
2.3.1 对象的创建
2.3.2 对象的内存布局
2.3.3 对象的访问定位
2.4 实战:OutOfMemoryError异常
2.4.1 Java堆溢出
2.4.2 虚拟机栈和本地方法栈溢出
2.4.3 方法区和运行时常量池溢出
2.4.4 本机直接内存溢出
2.5 本章小结
第3章 垃圾收集器与内存分配策略
3.1 概述
3.2 对象已死吗
3.2.1 引用计数算法
3.2.2 可达性分析算法
3.2.3 再谈引用
3.2.4 生存还是死亡
3.2.5 回收方法区
3.3 垃圾收集算法
3.3.1 标记-清除算法
3.3.2 复制算法
3.3.3 标记-整理算法
3.3.4 分代收集算法
3.4 HotSpot的算法实现
3.4.1 枚举根节点
3.4.2 安全点
3.4.3 安全区域
3.5 垃圾收集器
3.5.1 Serial收集器
3.5.2 ParNew收集器
3.5.3 Parallel Scavenge收集器
3.5.4 Serial Old收集器
3.5.5 Parallel Old收集器
3.5.6 CMS收集器
3.5.7 G1收集器
3.5.8 理解GC日志
3.5.9 垃圾收集器参数总结
3.6 内存分配与回收策略
3.6.1 对象优先在Eden分配
3.6.2 大对象直接进入老年代
3.6.3 长期存活的对象将进入老年代
3.6.4 动态对象年龄判定
3.6.5 空间分配担保
3.7 本章小结
第4章 虚拟机性能监控与故障处理工具
4.1 概述
4.2 JDK的命令行工具
4.2.1 jps:虚拟机进程状况工具
4.2.2 jstat:虚拟机统计信息监视工具
4.2.3 jinfo:Java配置信息工具
4.2.4 jmap:Java内存映像工具
4.2.5 jhat:虚拟机堆转储快照分析工具
4.2.6 jstack:Java堆栈跟踪工具
4.2.7 HSDIS:JIT生成代码反汇编
4.3 JDK的可视化工具
4.3.1 JConsole:Java监视与管理控制台
4.3.2 VisualVM:多合一故障处理工具
4.4 本章小结
第5章 调优案例分析与实战
5.1 概述
5.2 案例分析
5.2.1 高性能硬件上的程序部署策略
5.2.2 集群间同步导致的内存溢出
5.2.3 堆外内存导致的溢出错误
5.2.4 外部命令导致系统缓慢
5.2.5 服务器JVM进程崩溃
5.2.6 不恰当数据结构导致内存占用过大
5.2.7 由Windows虚拟内存导致的长时间停顿
5.3 实战:Eclipse运行速度调优
5.3.1 调优前的程序运行状态
5.3.2 升级JDK 1.6的性能变化及兼容问题
5.3.3 编译时间和类加载时间的优化
5.3.4 调整内存设置控制垃圾收集频率
5.3.5 选择收集器降低延迟
5.4 本章小结
第三部分 虚拟机执行子系统
第6章 类文件结构
6.1 概述
6.2 无关性的基石
6.3 Class类文件的结构
6.3.1 魔数与Class文件的版本
6.3.2 常量池
6.3.3 访问标志
6.3.4 类索引、父类索引与接口索引集合
6.3.5 字段表集合
6.3.6 方法表集合
6.3.7 属性表集合
6.4 字节码指令简介
6.4.1 字节码与数据类型
6.4.2 加载和存储指令
6.4.3 运算指令
6.4.4 类型转换指令
6.4.5 对象创建与访问指令
6.4.6 操作数栈管理指令
6.4.7 控制转移指令
6.4.8 方法调用和返回指令
6.4.9 异常处理指令
6.4.10 同步指令
6.5 公有设计和私有实现
6.6 Class文件结构的发展
6.7 本章小结
第7章 虚拟机类加载机制
7.1 概述
7.2 类加载的时机
7.3 类加载的过程
7.3.1 加载
7.3.2 验证
7.3.3 准备
7.3.4 解析
7.3.5 初始化
7.4 类加载器
7.4.1 类与类加载器
7.4.2 双亲委派模型
7.4.3 破坏双亲委派模型
7.5 本章小结
第8章 虚拟机字节码执行引擎
8.1 概述
8.2 运行时栈帧结构
8.2.1 局部变量表
8.2.2 操作数栈
8.2.3 动态连接
8.2.4 方法返回地址
8.2.5 附加信息
8.3 方法调用
8.3.1 解析
8.3.2 分派
8.3.3 动态类型语言支持
8.4 基于栈的字节码解释执行引擎
8.4.1 解释执行
8.4.2 基于栈的指令集与基于寄存器的指令集
8.4.3 基于栈的解释器执行过程
8.5 本章小结
第9章 类加载及执行子系统的案例与实战
9.1 概述
9.2 案例分析
9.2.1 Tomcat:正统的类加载器架构
9.2.2 OSGi:灵活的类加载器架构
9.2.3 字节码生成技术与动态代理的实现
9.2.4 Retrotranslator:跨越JDK版本
9.3 实战:自己动手实现远程执行功能
9.3.1 目标
9.3.2 思路
9.3.3 实现
9.3.4 验证
9.4 本章小结
第四部分 程序编译与代码优化
第10章 早期(编译期)优化
10.1 概述
10.2 Javac编译器
10.2.1 Javac的源码与调试
10.2.2 解析与填充符号表
10.2.3 注解处理器
10.2.4 语义分析与字节码生成
10.3 Java语法糖的味道
10.3.1 泛型与类型擦除
10.3.2 自动装箱、拆箱与遍历循环
10.3.3 条件编译
10.4 实战:插入式注解处理器
10.4.1 实战目标
10.4.2 代码实现
10.4.3 运行与测试
10.4.4 其他应用案例
10.5 本章小结
第11章 晚期(运行期)优化
11.1 概述
11.2 HotSpot虚拟机内的即时编译器
11.2.1 解释器与编译器
11.2.2 编译对象与触发条件
11.2.3 编译过程
11.2.4 查看及分析即时编译结果
11.3 编译优化技术
11.3.1 优化技术概览
11.3.2 公共子表达式消除
11.3.3 数组边界检查消除
11.3.4 方法内联
11.3.5 逃逸分析
11.4 Java与CC 的编译器对比
11.5 本章小结
第五部分 高效并发
第12章 Java内存模型与线程
12.1 概述
12.2 硬件的效率与一致性
12.3 Java内存模型
12.3.1 主内存与工作内存
12.3.2 内存间交互操作
12.3.3 对于volatile型变量的特殊规则
12.3.4 对于long和double型变量的特殊规则
12.3.5 原子性、可见性与有序性
12.3.6 先行发生原则
12.4 Java与线程
12.4.1 线程的实现
12.4.2 Java线程调度
12.4.3 状态转换
12.5 本章小结
第13章 线程安全与锁优化
13.1 概述
13.2 线程安全
13.2.1 Java语言中的线程安全
13.2.2 线程安全的实现方法
13.3 锁优化
13.3.1 自旋锁与自适应自旋
13.3.2 锁消除
13.3.3 锁粗化
13.3.4 轻量级锁
13.3.5 偏向锁
13.4 本章小结
附 录
附录A 编译Windows版的OpenJDK
附录B 虚拟机字节码指令表
附录C HotSpot虚拟机主要参数表
附录D 对象查询语言(OQL)简介
附录E JDK历史版本轨迹
前 言
Java是目前用户最多、使用范围最广的软件开发技术之一。Java的技术体系主要由支撑Java程序运行的虚拟机、提供各开发领域接口支持的JavaAPI、Java编程语言及许多第三方Java框架(如Spring、Struts等)构成。在国内,有关JavaAPI、Java语言语法及第三方框架的技术资料和书籍非常丰富,相比之下,有关Java虚拟机的资料却显得异常贫乏。
这种状况在很大程度上是由Java开发技术本身的一个重要优点导致的:在虚拟机层面隐藏了底层技术的复杂性以及机器与操作系统的差异性。运行程序的物理机器的情况千差万别,而Java虚拟机则在千差万别的物理机上建立了统一的运行平台,实现了在任意一台虚拟机上编译的程序都能在任何一台虚拟机上正常运行。这一极大优势使得Java应用的开发比传统C/C 应用的开发更高效和快捷,程序员可以把主要精力集中在具体业务逻辑上,而不是物理硬件的兼容性上。在一般情况下,一个程序员只要了解了必要的JavaAPI、Java语法,以及学习适当的第三方开发框架,就已经基本能满足日常开发的需要了,虚拟机会在用户不知不觉中完成对硬件平台的兼容及对内存等资源的管理工作。因此,了解虚拟机的运作并不是一般开发人员必须掌握的知识。
然而,凡事都具备两面性。随着Java技术的不断发展,它被应用于越来越多的领域之中。其中一些领域,如电力、金融、通信等,对程序的性能、稳定性和可扩展性方面都有极高的要求。程序很可能在10个人同时使用时完全正常,但是在10?000个人同时使用时就会缓慢、死锁,甚至崩溃。毫无疑问,要满足10?000个人同时使用需要更高性能的物理硬件,但是在绝大多数情况下,提升硬件效能无法等比例地提升程序的运作性能和并发能力,甚至可能对程序运作状况完全没有任何改善。这里面有Java虚拟机的原因:为了达到给所有硬件提供一致的虚拟平台的目的,牺牲了一些与硬件相关的性能特性。更重要的是人为原因:如果开发人员不了解虚拟机一些技术特性的运行原理,就无法写出最适合虚拟机运行和自优化的代码。
其实,目前商用的高性能Java虚拟机都提供了相当多的优化特性和调节手段,用于满足应用程序在实际生产环境中对性能和稳定性的要求。如果只是为了入门学习,让程序在自己的机器上正常运行,那么这些特性可以说是可有可无的;如果用于生产开发,尤其是企业级生产开发,就迫切需要开发人员中至少有一部分人对虚拟机的特性及调节方法具有很清晰的认识,所以在Java开发体系中,对架构师、系统调优师、高级程序员等角色的需求一直都非常大。学习虚拟机中各种自动运作特性的原理也成为了Java程序员成长道路上必然会接触到的一课。本书可以使读者以一种相对轻松的方式学习虚拟机的运作原理,对Java程序员的成长也有较大的帮助。
第2版与第1版的区别
JDK 1.7在2011年7月28日正式发布,相对于2006年发布的JDK1.6,新版的JDK有了许多新的特性和改进。本书的第2版也相应地进行了修改和升级,把讲解的技术平台从JDK 1.6提升至JDK1.7。例如,增加了对JDK 1.7中最新的G1收集器,以及JDK 1.7中JSR-292InvokeDynamic(对非Java语言的调用支持)的分析讲解等内容。
在第1版出版后,笔者收到了许多热心读者的反馈意见,部分读者提出OpenJDK开源已久,第1版却很少有直接分析OpenJDK源码的内容,有点“视宝山而不见”的感觉。因此,在本书第2版中,笔者特别加强了对这部分内容的讲解,其中在第1章中就介绍了如何分析、调试OpenJDK源码等。在本书后续章节中,不少关于功能点的讲解都直接使用OpenJDK中的HotSpot源码或者JIT编译器生成的本地代码作为论据。
如何把Java虚拟机原理中许多理论性很强的知识、特性应用于实践开发,是本书贯穿始终的主旨。由于笔者希望在本书第2版中进一步加强知识的实践性,因此增加了许多对处理JVM常见问题技能的讲解,包括如何分析GC日志、如何分析JIT编译器代码优化过程和生成代码等。并且,在第1版的基础上,第2版中进一步增加了若干处理JVM问题的实践案例供读者参考。
另外,本书第2版还修正了第1版中多处错误的、有歧义的和不完整的描述。有关勘误信息,可以参考第1版的勘误页面()。
本书面向的读者
(1)使用Java技术体系的中、高级开发人员
Java虚拟机作为中、高级开发人员必须修炼的知识,有着较高的学习门槛,本书可作为学习虚拟机的优秀教材。
(2)系统调优师
系统调优师是近几年才兴起的职业,本书中的大量案例、代码和调优实战将会对系统调优师的日常工作有直接的帮助。
(3)系统架构师
保障系统的性能、并发和伸缩等能力是系统架构师的主要职责之一,而这部分与虚拟机的运作密不可分,本书可以作为他们制定应用系统底层框架的参考资料。
如何阅读本书
本书一共分为五个部分:走近Java、自动内存管理机制、虚拟机执行子系统、程序编译与代码优化、高效并发。各部分基本上是互相独立的,没有必然的前后依赖关系,读者可以从任何一个感兴趣的专题开始阅读,但是每个部分中的各个章节间有先后顺序。
本书并没有假设读者在Java领域具备很专业的技术水平,因此在保证逻辑准确的前提下,尽量用通俗的语言和案例讲述虚拟机中与开发的关系最为密切的内容。当然,学习虚拟机技术本身就需要读者有一定的基础,且本书的读者定位是中、高级程序员,因此本书假设读者自己了解一些常用的开发框架、JavaAPI和Java语法等基础知识。
笔者希望读者在阅读本书的同时,把本书中的实践内容亲自验证一遍,其中用到的代码清单可以从华章网站()下载。
语言约定
本书在语言和技术上有如下约定:
?本书中提到HotSpot、JRockit虚拟机、WebLogic服务器等产品的所有者时,仍然使用Sun和BEA公司的名称,实际上,BEA和Sun分别于2008年和2009年被Oracle公司收购,现在已经不存在这两个商标了,但毫无疑问的是,它们都是在Java领域中做出过卓越贡献的、值得程序员纪念的公司。
?JDK从1.5版本开始,在官方的正式文档与宣传资料中已经不再使用类似“JDK1.5”的名称,只有程序员内部使用的开发版本号(DeveloperVersion,例如java-version的输出)才继续沿用1.5、1.6和1.7的版本号,而公开版本号(ProductVersion)则改为JDK 5、JDK 6和JDK7的命名方式,为了行文一致,本书所有场合统一采用开发版本号的命名方式。
?由于版面关系,本书中的许多示例代码都没有遵循最优的代码编写风格,如使用的流没有关闭流等,请读者在阅读时注意这一点。
?如果没有特殊说明,本书中所有讨论都是以Sun JDK1.7为技术平台的。不过如果有某个特性在各个版本间的变化较大,一般都会说明它在各个版本间的差异。
内容特色
第一部分 走近Java
本书的第一部分为后文的讲解建立了良好的基础。尽管了解Java技术的来龙去脉,以及编译自己的OpenJDK对于读者理解Java虚拟机并不是必需的,但是这些准备过程可以为走近Java技术和Java虚拟机提供很好的引导。第一部分只有第1章:
第1章 介绍了Java技术体系的过去、现在和未来的一些发展趋势,并介绍了如何独立地编译一个OpenJDK 7。
第二部分 自动内存管理机制
因为程序员把内存控制的权力交给了Java虚拟机,所以可以在编码的时候享受自动内存管理的诸多优势,不过也正是这个原因,一旦出现内存泄漏和溢出方面的问题,如果不了解虚拟机是怎样使用内存的,那么排查错误将会成为一项异常艰难的工作。第二部分包括第2~5章:
第2章 讲解了虚拟机中内存是如何划分的,以及哪部分区域、什么样的代码和操作可能导致内存溢出异常,并讲解了各个区域出现内存溢出异常的常见原因。
第3章 分析了垃圾收集的算法和JDK1.7中提供的几款垃圾收集器的特点及运作原理。通过代码实例验证了Java虚拟机中自动内存分配及回收的主要规则。
第4章 介绍了随JDK发布的6个命令行工具与两个可视化的故障处理工具的使用方法。
第5章 与读者分享了几个比较有代表性的实际案例,还准备了一个所有开发人员都能“亲身实战”的练习,读者可通过实践来获得故障处理和调优的经验。
第三部分 虚拟机执行子系统
执行子系统是虚拟机中必不可少的组成部分,了解了虚拟机如何执行程序,才能写出更优秀的代码。第三部分包括第6~9章:
第6章 讲解了Class文件结构中的各个组成部分,以及每个部分的定义、数据结构和使用方法,以实战的方式演示了Class文件的数据是如何存储和访问的。
第7章 介绍了类加载过程的“加载”、“验证”、“准备”、“解析”和“初始化”5个阶段中虚拟机分别执行了哪些动作,还介绍了类加载器的工作原理及其对虚拟机的意义。
第8章 分析了虚拟机在执行代码时如何找到正确的方法,如何执行方法内的字节码,以及执行代码时涉及的内存结构。
第9章 通过4个类加载及执行子系统的案例,分享了使用类加载器和处理字节码的一些值得欣赏和借鉴的思路,并通过一个实战练习来加深对前面理论知识的理解。
第四部分 程序编译与代码优化
Java程序从源码编译成字节码和从字节码编译成本地机器码的这两个过程,合并起来其实就等同于一个传统编译器所执行的编译过程。第四部分包括第10~11章:
第10章 分析了Java语言中泛型、主动装箱和拆箱、条件编译等多种语法糖的前因后果,并通过实战演示了如何使用插入式注解处理器来实现一个检查程序命名规范的编译器插件。
第11章 讲解了虚拟机的热点探测方法、HotSpot的即时编译器、编译触发条件,以及如何从虚拟机外部观察和分析JIT编译的数据和结果,此外,还讲解了几种常见的编译优化技术。
第五部分 高效并发
Java语言和虚拟机提供了原生的、完善的多线程支持,这使得它天生就适合开发多线程并发的应用程序。不过我们不能期望系统来完成所有并发相关的处理,了解并发的内幕也是成为一个高级程序员不可缺少的课程。第五部分包括第12~13章:
第12章 讲解了虚拟机Java内存模型的结构及操作,以及原子性、可见性和有序性在Java内存模型中的体现,介绍了先行发生原则的规则及使用,还了解了线程在Java语言中是如何实现的。
第13章 介绍了线程安全涉及的概念和分类、同步实现的方式及虚拟机的底层运作原理,并且介绍了虚拟机实现高效并发所采取的一系列锁优化措施。
参考资料
本书名为“深入理解Java虚拟机”,但要想深入理解虚拟机,仅凭一本书肯定是远远不够的,读者可以通过以下信息找到更多关于Java虚拟机方面的资料。我在写作此书的时候,也从下面这些参考资料中获得了很大的帮助。
(1)书籍
?《The Java Virtual Machine Specification,Java SE7 Edition》
要学习虚拟机,无论如何都必须掌握“Java虚拟机规范”。这本书的概念和细节描述与Sun的早期虚拟机(Sun ClassicVM)高度吻合,不过,随着技术的发展,高性能虚拟机真正的细节实现方式已经渐渐与虚拟机规范所描述的差距越来越大,如果只能选择一本参考书来了解虚拟机,那我推荐这本书。此书的JavaSE 7版在2011年7月出版发行,这是自1999年发布的《Java虚拟机规范(第2版)》以来的第一次版本更新。笔者对Java SE7版的全文进行了翻译,并与原书一样在网上免费发布了全文PDF。
?《The Java Language Specification,Java SE 7Edition》
虽然虚拟机并不是Java语言专有的,但是了解Java语言的各种细节规定对理解虚拟机的行为也是很有帮助的,它与上一本《Java虚拟机规范》都是Sun官方出品的书籍,而且这本书还是由Java之父JamesGosling亲自执笔撰写的。这本书也与《Java虚拟机规范》一样,可以在官方网站完全免费下载到全文PDF,但暂时没有中文译本,《Java语言规范(第3版)》于2005年7月由机械工业出版社引进出版。
?《Oracle JRockit The Definitive Guide》
《OracleJRockit权威指南》,2010年7月出版,国内也没有(可能是尚未)引进这本书,它是由JRockit的两位资深开发人员(其中一位还是JRockitMissionControl团队的TeamLeader)撰写的JRockit虚拟机高级使用指南。虽然JRockit的用户量可能不如HotSpot多,但也是目前最流行的三大商业虚拟机之一,并且不同虚拟机中的很多实现思路都是可以对比参照的。这本书是了解现代高性能虚拟机很好的参考资料。
?《Inside the Java 2 Virtual Machine,SecondEdition》
《深入Java虚拟机(第2版)》,2000年1月出版,2003年由机械工业出版社出版其中文译本。在相当长的时间里,这本书是唯一的一本关于Java虚拟机的中文图书。
?《Java Performance》
《Java Performance》是“TheJava”系列(许多人都读过该系列中最出名的《EffectiveJava》)图书中最新的一本,2011年10月出版,暂时没有中文版。这本书并非全部都围绕Java虚拟机(只有第3、4、7章直接与Java虚拟机相关),而是从操作系统到基于Java的上层程序性能度量和调优的全面介绍,其中涉及Java虚拟机的内容具备一定的深度和可实践性。
(2)网站资源
?高级语言虚拟机圈子:
里面有一些国内关于虚拟机的讨论,并不只限于JVM,而是涉及对所有的高级语言虚拟机(High-Level LanguageVirtualMachine)的讨论,但该网站建立在ITEye上,自然还是以讨论Java虚拟机为主。圈主RednaxelaFX(莫枢)的博客()是另外一个非常有价值的虚拟机及编译原理等资料的分享园地。
?HotSpot Internals:
一个关于OpenJDK的Wiki网站,许多文章都由JDK的开发团队编写,更新较慢,但是仍然有很高的参考价值。
?The HotSpotGroup:
HotSpot组群,包含虚拟机开发、编译器、垃圾收集和运行时4个邮件组,其中有关于HotSpot虚拟机的最新讨论。
勘误和支持
在本书交稿的时候,我并不像想象中的那样兴奋或放松,写作之时那种“战战兢兢、如履薄冰”的感觉依然萦绕在心头。在每一章、每一节落笔之时,我都在考虑如何才能把各个知识点更有条理地讲述出来,同时也在担心会不会由于自己理解有偏差而误导了读者。由于写作水平和写作时间所限,书中难免存在不妥之处,所以特地开通了一个读者邮箱()与大家交流,大家如有任何意见或建议欢迎与我联系。相信写书与写程序一样,作品一定都是不完美的,因为不完美,我们才有不断追求完美的动力。
本书第2版的勘误,将会在作者的博客()中发布。欢迎读者在博客上留言。
致谢
首先要感谢我的家人,在本书写作期间全靠他们对我的悉心照顾,才让我能够全身心地投入到写作之中,而无后顾之忧。
同时要感谢我的工作单位远光软件,公司为我提供了宝贵的工作、学习和实践的环境,书中的许多知识点都来自于工作中的实践;也感谢与我一起工作的同事们,非常荣幸能与你们一起在这个富有激情的团队中共同奋斗。
还要感谢Oracle公司虚拟机团队的莫枢,在百忙之中抽空审阅了本书,提出了许多宝贵的建议和意见。
最后,感谢机械工业出版社华章公司的编辑,本书能够顺利出版离不开他们的敬业精神和一丝不苟的工作态度。
周志明
第一部分
走近Java
第1章 走近Java
第1章 走近Java
世界上并没有完美的程序,但我们并不因此而沮丧,因为写程序本来就是一个不断追求完美的过程。
1.1 概述
Java不仅仅是一门编程语言,还是一个由一系列计算机软件和规范形成的技术体系,这个技术体系提供了完整的用于软件开发和跨平台部署的支持环境,并广泛应用于嵌入式系统、移动终端、企业服务器、大型机等各种场合,如图1-1所示。时至今日,Java技术体系已经吸引了900多万软件开发者,这是全球最大的软件开发团队。使用Java的设备多达几十亿台,其中包括11亿多台个人计算机、30亿部移动电话及其他手持设备、数量众多的智能卡,以及大量机顶盒、导航系统和其他设备。
Java能获得如此广泛的认可,除了它拥有一门结构严谨、面向对象的编程语言之外,还有许多不可忽视的优点:它摆脱了硬件平台的束缚,实现了“一次编写,到处运行”的理想;它提供了一个相对安全的内存管理和访问机制,避免了绝大部分的内存泄露和指针越界问题;它实现了热点代码检测和运行时编译及优化,这使得Java应用能随着运行时间的增加而获得更高的性能;它有一套完善的应用程序接口,还有无数来自商业机构和开源社区的第三方类库来帮助它实现各种各样的功能……Java所带来的这些好处使程序的开发效率得到了很大的提升。作为一名Java程序员,在编写程序时除了尽情发挥Java的各种优势外,还应该去了解和思考一下Java技术体系中这些技术特性是如何实现的。认识这些技术运作的本质,是自己思考“程序这样写好不好”的基础和前提。当我们在使用一种技术时,如果不再依赖书本和他人就能得到这些问题的答案,那才算上升到了“不惑”的境界。
本书将与读者一起分析Java技术中最重要的那些特性的实现原理。在本章中,我们将重点介绍Java技术体系内容以及Java的历史、现在和未来的发展趋势。
1.2 Java技术体系
从广义上讲,Clojure、JRuby、Groovy等运行于Java虚拟机上的语言及其相关的程序都属于Java技术体系中的一员。如果仅从传统意义上来看,Sun官方所定义的Java技术体系包括以下几个组成部分:
?Java程序设计语言
?各种硬件平台上的Java虚拟机
?Class文件格式
?Java API类库
?来自商业机构和开源社区的第三方Java类库
我们可以把Java程序设计语言、Java虚拟机、Java API类库这三部分统称为JDK(Java DevelopmentKit),JDK是用于支持Java程序开发的最小环境,在后面的内容中,为了讲解方便,有一些地方会以JDK来代替整个Java技术体系。另外,可以把JavaAPI类库中的Java SE API子集和Java虚拟机这两部分统称为JRE(Java RuntimeEnvironment),JRE是支持Java程序运行的标准环境。图1-2展示了Java技术体系所包含的内容,以及JDK和JRE所涵盖的范围。
以上是根据各个组成部分的功能来进行划分的,如果按照技术所服务的领域来划分,或者说按照Java技术关注的重点业务领域来划分,Java技术体系可以分为4个平台,分别为:
?Java Card:支持一些Java小程序(Applets)运行在小内存设备(如智能卡)上的平台。
?Java ME(Micro Edition):支持Java程序运行在移动终端(手机、PDA)上的平台,对JavaAPI有所精简,并加入了针对移动终端的支持,这个版本以前称为J2ME。
?Java SE(StandardEdition):支持面向桌面级应用(如Windows下的应用程序)的Java平台,提供了完整的Java核心API,这个版本以前称为J2SE。
?Java EE(EnterpriseEdition):支持使用多层架构的企业应用(如ERP、CRM应用)的Java平台,除了提供Java SEAPI外,还对其做了大量的扩充并提供了相关的部署支持,这个版本以前称为J2EE。
1.3 Java发展史
从第一个Java版本诞生到现在已经有18年的时间了。沧海桑田一瞬间,转眼18年过去了,在图1-3所展示的时间线中,我们看到JDK已经发展到了1.7版。在这18年里还诞生了无数和Java相关的产品、技术和标准。现在让我们走入时间隧道,从孕育Java语言的时代开始,再来回顾一下Java的发展轨迹和历史变迁。
1991年4月,由James Gosling博士领导的绿色计划(GreenProject)开始启动,此计划的目的是开发一种能够在各种消费性电子产品(如机顶盒、冰箱、收音机等)上运行的程序架构。这个计划的产品就是Java语言的前身:Oak(橡树)。Oak当时在消费品市场上并不算成功,但随着1995年互联网潮流的兴起,Oak迅速找到了最适合自己发展的市场定位并蜕变成为Java语言。
1995年5月23日,Oak语言改名为Java,并且在SunWorld大会上正式发布Java1.0版本。Java语言第一次提出了“Write Once,Run Anywhere”的口号。
1996年1月23日,JDK 1.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公司发布了JDK 1.1,Java技术的一些最基础的支撑点(如JDBC等)都是在JDK1.1版本中发布的,JDK1.1版的技术代表有:JAR文件格式、JDBC、JavaBeans、RMI。Java语法也有了一定的发展,如内部类(InnerClass)和反射(Reflection)都是在这个时候出现的。
直到1999年4月8日,JDK1.1一共发布了1.1.0~1.1.8九个版本。从1.1.4之后,每个JDK版本都有一个自己的名字(工程代号),分别为:JDK1.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迎来了一个里程碑式的版本JDK1.2,工程代号为Playground(竞技场),Sun在这个版本中把Java技术体系拆分为3个方向,分别是面向桌面应用开发的J2SE(Java2 Platform, Standard Edition)、面向企业级开发的J2EE(Java 2 Platform,Enterprise Edition)和面向手机等移动终端开发的J2ME(Java 2 Platform, MicroEdition)。在这个版本中出现的代表性技术非常多,如EJB、Java Plug-in、JavaIDL、Swing等,并且这个版本中Java虚拟机第一次内置了JIT(Just In Time)编译器(JDK1.2中曾并存过3个虚拟机,Classic VM、HotSpot VM和Exact VM,其中ExactVM只在Solaris平台出现过;后面两个虚拟机都是内置JIT编译器的,而之前版本所带的ClassicVM只能以外挂的形式使用JIT编译器)。在语言和API级别上,Java添加了strictfp关键字与现在Java编码之中极为常用的一系列Collections集合类。在1999年3月和7月,分别有JDK1.2.1和JDK 1.2.2两个小版本发布。
1999年4月27日,HotSpot虚拟机发布,HotSpot最初由一家名为“LongviewTechnologies”的小公司开发,因为HotSpot的优异表现,这家公司在1997年被Sun公司收购了。HotSpot虚拟机发布时是作为JDK1.2的附加程序提供的,后来它成为了JDK 1.3及之后所有版本的Sun JDK的默认虚拟机。
2000年5月8日,工程代号为Kestrel(美洲红隼)的JDK 1.3发布,JDK 1.3相对于JDK1.2的改进主要表现在一些类库上(如数学运算和新的Timer API等),JNDI服务从JDK1.3开始被作为一项平台级服务提供(以前JNDI仅仅是一项扩展),使用CORBAIIOP来实现RMI的通信协议,等等。这个版本还对Java 2D做了很多改进,提供了大量新的Java 2DAPI,并且新添加了JavaSound类库。JDK 1.3有1个修正版本JDK1.3.1,工程代号为Ladybird(瓢虫),于2001年5月17日发布。
自从JDK1.3开始,Sun维持了一个习惯:大约每隔两年发布一个JDK的主版本,以动物命名,期间发布的各个修正版本则以昆虫作为工程名称。
2002年2月13日,JDK 1.4发布,工程代号为Merlin(灰背隼)。JDK1.4是Java真正走向成熟的一个版本,Compaq、Fujitsu、SAS、Symbian、IBM等著名公司都有参与甚至实现自己独立的JDK1.4。哪怕是在十多年后的今天,仍然有许多主流应用(Spring、Hibernate、Struts等)能直接运行在JDK1.4之上,或者继续发布能运行在JDK 1.4上的版本。JDK1.4同样发布了很多新的技术特性,如正则表达式、异常链、NIO、日志类、XML解析器和XSLT转换器等。JDK1.4有两个后续修正版:2002年9月16日发布的工程代号为Grasshopper(蚱蜢)的JDK1.4.1与2003年6月26日发布的工程代号为Mantis(螳螂)的JDK 1.4.2。
2002年前后还发生了一件与Java没有直接关系,但事实上对Java的发展进程影响很大的事件,那就是微软公司的.NETFramework发布了。这个无论是技术实现上还是目标用户上都与Java有很多相近之处的技术平台给Java带来了很多讨论、比较和竞争,.NET平台和Java平台之间声势浩大的孰优孰劣的论战到目前为止都在继续。
2004年9月30日,JDK 1.5发布,工程代号Tiger(老虎)。从JDK1.2以来,Java在语法层面上的变换一直很小,而JDK1.5在Java语法易用性上做出了非常大的改进。例如,自动装箱、泛型、动态注解、枚举、可变长参数、遍历循环(foreach循环)等语法特性都是在JDK1.5中加入的。在虚拟机和API层面上,这个版本改进了Java的内存模型(Java MemoryModel,JMM)、提供了java.util.concurrent并发包等。另外,JDK 1.5是官方声明可以支持Windows9x平台的最后一个JDK版本。
2006年12月11日,JDK 1.6发布,工程代号Mustang(野马)。在这个版本中,Sun终结了从JDK1.2开始已经有8年历史的J2EE、J2SE、J2ME的命名方式,启用Java SE 6、Java EE 6、Java ME6的命名方式。JDK 1.6的改进包括:提供动态语言支持(通过内置Mozilla JavaScriptRhino引擎实现)、提供编译API和微型HTTP服务器API等。同时,这个版本对Java虚拟机内部做了大量改进,包括锁与同步、垃圾收集、类加载等方面的算法都有相当多的改动。
在2006年11月13日的JavaOne大会上,Sun公司宣布最终会将Java开源,并在随后的一年多时间内,陆续将JDK的各个部分在GPLv2(GNU General Public Licensev2)协议下公开了源码,并建立了OpenJDK组织对这些源码进行独立管理。除了极少量的产权代码(EncumberedCode,这部分代码大多是Sun本身也无权限进行开源处理的)外,OpenJDK几乎包括了SunJDK的全部代码,OpenJDK的质量主管曾经表示,在JDK 1.7中,SunJDK和OpenJDK除了代码文件头的版权注释之外,代码基本上完全一样,所以OpenJDK 7与Sun JDK1.7本质上就是同一套代码库开发的产品。
JDK1.6发布以后,由于代码复杂性的增加、JDK开源、开发JavaFX、经济危机及Sun收购案等原因,Sun在JDK发展以外的事情上耗费了很多资源,JDK的更新没有再维持两年发布一个主版本的发展速度。JDK1.6到目前为止一共发布了37个Update版本,最新的版本为Java SE 6 Update37,于2012年10月16日发布。
2009年2月19日,工程代号为Dolphin(海豚)的JDK 1.7完成了其第一个里程碑版本。根据JDK1.7的功能规划,一共设置了10个里程碑。最后一个里程碑版本原计划于2010年9月9日结束,但由于各种原因,JDK1.7最终无法按计划完成。
从JDK1.7最开始的功能规划来看,它本应是一个包含许多重要改进的JDK版本,其中的Lambda项目(Lambda表达式、函数式编程)、Jigsaw项目(虚拟机模块化支持)、动态语言支持、GarbageFirst收集器和Coin项目(语言细节进化)等子项目对于Java业界都会产生深远的影响。在JDK1.7开发期间,Sun公司由于相继在技术竞争和商业竞争中都陷入泥潭,公司的股票市值跌至仅有高峰时期的3%,已无力推动JDK1.7的研发工作按正常计划进行。为了尽快结束JDK1.7长期“跳票”的问题,Oracle公司收购Sun公司后不久便宣布将实行“B计划”,大幅裁剪了JDK 1.7预定目标,以便保证JDK1.7的正式版能够于2011年7月28日准时发布。“B计划”把不能按时完成的Lambda项目、Jigsaw项目和Coin项目的部分改进延迟到JDK1.8之中。最终,JDK1.7的主要改进包括:提供新的G1收集器(G1在发布时依然处于Experimental状态,直至2012年4月的Update4中才正式“转正”)、加强对非Java语言的调用支持(JSR-292,这项特性到目前为止依然没有完全实现定型)、升级类加载架构等。
到目前为止,JDK 1.7已经发布了9个Update版本,最新的Java SE 7 Update9于2012年10月16日发布。从Java SE 7 Update 4起,Oracle开始支持Mac OSX操作系统,并在Update 6中达到完全支持的程度,同时,在Update6中还对ARM指令集架构提供了支持。至此,官方提供的JDK可以运行于Windows(不含Windows9x)、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将会提供在JDK1.7中规划过,但最终未能在JDK1.7中发布的特性,即Lambda表达式、Jigsaw(很不幸,随后Oracle公司又宣布Jigsaw在JDK1.8中依然无法完成,需要延至JDK 1.9)和JDK 1.7中未实现的一部分Coin等。
在2011年的JavaOne大会上,Oracle公司还提到了JDK1.9的长远规划,希望未来的Java虚拟机能够管理数以GB计的Java堆,能够更高效地与本地代码集成,并且令Java虚拟机运行时尽可能少人工干预,能够自动调节。
1.4 Java虚拟机发展史
上一节我们从整个Java技术的角度观察了Java技术的发展,许多Java程序员都会潜意识地把它与Sun公司的HotSpot虚拟机等同看待,也许还有一些程序员会注意到BEAJRockit和IBM J9,但对JVM的认识不仅仅只有这些。
从1996年初Sun公司发布的JDK 1.0中所包含的Sun ClassicVM到今天,曾经涌现、湮灭过许多或经典或优秀或有特色的虚拟机实现,在这一节中,我们先暂且把代码与技术放下,一起来回顾一下Java虚拟机家族的发展轨迹和历史变迁。
1.4.1 Sun Classic / Exact VM
以今天的视角来看,Sun ClassicVM的技术可能很原始,这款虚拟机的使命也早已终结。但仅凭它“世界上第一款商用Java虚拟机”的头衔,就足够有让历史记住它的理由。
1996年1月23日,Sun公司发布JDK1.0,Java语言首次拥有了商用的正式运行环境,这个JDK中所带的虚拟机就是ClassicVM。这款虚拟机只能使用纯解释器方式来执行Java代码,如果要使用JIT编译器,就必须进行外挂。但是假如外挂了JIT编译器,JIT编译器就完全接管了虚拟机的执行系统,解释器便不再工作了。用户在这款虚拟机上执行java-version命令,将会看到类似下面这行输出:
java version “1.2.2”
Classic VM (build JDK-1.2.2-001, green threads, sunwjit)
其中的“sunwjit”就是Sun提供的外挂编译器,其他类似的外挂编译器还有SymantecJIT和shuJIT等。由于解释器和编译器不能配合工作,这就意味着如果要使用编译器执行,编译器就不得不对每一个方法、每一行代码都进行编译,而无论它们执行的频率是否具有编译的价值。基于程序响应时间的压力,这些编译器根本不敢应用编译耗时稍高的优化技术,因此这个阶段的虚拟机即使用了JIT编译器输出本地代码,执行效率也和传统的C/C 程序有很大差距,“Java语言很慢”的形象就是在这时候开始在用户心中树立起来的
星星草 –
讲得深入浅出,把虚拟机内部的算法说得很通透,具体的调优实践也举例说明得比较直观,值得一看
结局接近开始 –
不得不说,作者对虚拟机讲解的非常好,难怪大家都说好,不看不知道。 只是这本书面对的用户是中高级别的
乱 –
买了读了第二章,加深了对虚拟机的了解,看起来比较流畅。