1.4 Java生态的体系结构

本节介绍Java技术生态中的结构组成部分。从宏观角度来看,Java的体系结构主要包括4个独立的部分:

(1)类装载系统。

(2)Class字节码文件。

(3)Java应用程序接口。

(4)JVM。

通过上述4个组件的相互配合,组成了Java程序从0到1的运行流程。Java程序的源代码要通过编译器进行编译,才能生成对应的Class字节码,之后通过类装载器对其进行加载处理,生成一个JVM的运行时数据模型,最后调用Java应用程序接口,从而实现对资源的访问。

1.4.1 类装载系统

类装载系统是JVM技术中非常重要的一个环节,可以将其理解为一个能将可授信的“数据信息”引入JVM内部的桥梁和网关,如图1.1所示。

图1.1 类装载系统的桥梁作用

同时类装载系统也对JVM的安全性和网络移动性的发展起着很大的作用,它负责JVM的安全保障,第一道安全防线是JVM的类的加载阶段,毕竟字节码都是由类加载器来装入JVM中的,其中也包括未授信的代码,其安全控制主要有3点。

(1)保护Java系统内部核心的运作机制不会被外界的恶意代码所控制或侵入干扰,可以实现舱壁的隔离机制。因此,类加载系统能够通过不同的类加载器加载可信代码和不信任代码,进而保障可信包的安全性。

(2)保护Java系统中已验证的类库,以避免开发人员覆盖核心类库,从而篡改整体核心库的功能实现,主要体现在双亲委托模型。

(3)开发者可以对Class字节码进行加密,同时在类加载阶段对其进行解密,从而实现定制化对代码的保护和控制。

类加载器作为类加载系统的实际执行者,主要有两种类型:启动型加载器(属于JVM实现的一部分)和Java实现的类加载器(扩展型加载器、系统型加载器和自定义加载器等),后文会针对这几种类加载器进行详细的介绍和分析。以上类加载器默认采用双亲委托模型的方式,防止同一个类被多个类加载器加载多次,以及防止核心类库被其他类加载器进行覆盖或重写。

类加载器本身采用命名空间的隔离机制,不同的类加载器会采用不同的命名空间进行隔离,不同的命名空间内部的类无法相互访问,除非用户采用显式调用的方式进行访问。

最后,类加载器对网络移动性的支持,主要依靠自定义类加载器来装载不同来源的Class字节码。例如,可以通过网络传输下载其他服务器上的Class字节码文件,并将其加载到JVM内部。

类加载器装载Class字节码的总体过程分为3个阶段:加载(读取)阶段、链接阶段和初始化阶段,如图1.2所示。

图1.2 类加载阶段

1.4.2 Class字节码文件

通过上面内容的学习,我们了解Java体系有一项十分关键的特点是跨平台性,这说明只要完成编译任务,Java代码就能够在其他平台上顺畅执行。而跨平台的运行标准就是Class字节码文件, Class字节码是提供平台无关性的基础模型,使我们无须考虑如何兼容异构系统,它只须被JVM所识别即可。

此外,Class字节码文件对网络移动性也有很大作用。首先,它的文件结构被设计得非常紧凑和严密,从网络传输角度而言,所占资源特别少。所以,从其他服务器下载Class字节码时,延迟的时间会很少。其次,在加载Class文件时,如果存在动态链接或者动态扩展的代码实现,那么此部分代码可以让解析阶段(Resolve)延后执行,这就允许程序可以在运行时下载相关的Class文件并装载,大大提升了运行的效率并缩短用户的延迟等待时间。

1.4.3 Java应用程序接口

什么是Java应用程序接口呢?在日常程序开发中,我们用到的最多的类库是Java SE的API(此处暂时不考虑基于Java EE和基于JavaME 一类的API),API实际上是平台已经编写好、能够直接提供开发调用的类库,称为应用程序接口。其本身支持平台无关性和安全性。

运行Java应用程序时,JVM首先通过启动型类加载器加载所有Java SE的核心API库及本地方法库,它们共同组成了JVM的核心程序,与此同时会加载应用程序所需的Class类。

从安全性角度而言,API还支持通过安全管理器(Security Manager)和访问控制器(Access Controller)对方法和资源进行安全控制。

平台无关性主要通过门面模式和策略模式的原理来实现,所以针对每种系统平台都明确地实现了JVM和Java API。例如,由于Java API已经调用了Native方法,因此客户程序就不需再调用底层的本地方法,而是直接通过Java API进行调用,从而实现底层处理的平台无关性,执行统一的标准接口。

1.4.4 认识Java虚拟机(JVM)

Java体系中最重要的“中枢大脑”就是JVM,同时它也是本书中的重点核心。JVM如同一台计算机,拥有自己的协议和标准,其他厂商无论采用哪种方案实现虚拟机,都不能脱离它的标准。其规范的核心目的就是要兼容不同系统架构,使它们的表现形式基本相同。无论是装载Class文件的统一化,还是执行指令的标准化,都是屏蔽差异化实现跨平台性的手段。

当之前所有的环节都完成了统一化和标准化后,即可执行Java程序,通过Java解释器和Java编译器(JIT编译器和AOT编译器)的方式对字节码指令进行执行。虚拟机内部结构非常的复杂,后续会对其结构和相关特性做详细的介绍。

1.4.5 Java体系的得与失

上文重点介绍的是Java体系的优点,本节将重点介绍Java的不足之处。

(1)执行性能和速度不足,这是由于追求Java语言的动态性和跨平台性,以及允许在程序运行时动态加载或者动态扩展导致的。JVM在执行方法的过程中包含编译源代码、解释字节码、执行机器指令等过程,执行过程较为复杂,因此与C或者C++相比速度会下降很多。目前这个差距已经在慢慢缩小,随着JIT编译器和AOT编译器等的引入,其在有限的范围内已经不断地接近C++的执行速度。

(2)Java因支持分布式系统,从而导致复杂度变高。相对一个单体系统而言,分布式系统无论在功能管理还是数据的安全性与一致性方面,都存在复杂化和不稳定的趋势,如何避免及解决这些问题,将是Java未来发展的一个重要课题。

(3)Java不支持底层操作及系统内存的灵活控制,因为这些都交由JVM进行统一化管理了。虽然这样会使得开发者只需要关注开发的功能或者业务场景,但这也同时丧失了对底层数据和内存的控制。

当然Java还有其他的缺点,但总体而言,以上这3点属于较为典型的问题,故予以列出。