位于上海,服务全国!

位于上海,服务全国!

理解JVM加载,链接和初始化

作者:admin 分类: 时间:2017-11-15 20:52:44 点击量:1192

Java虚拟机(JVM)在幕后执行大量工作,其运行被提交的二进制类文件。但不只是那样简单,因为它似乎就像使用java命令调用JVM,并将class文件作为命令行参数传递给它。类文件编译有许多阶段过程,但在这里我们只介绍最初的三个执行阶段,即加载,链接和初始化过程。即便如此,这也是一个大而复杂的话题。因此,这里只是简要地描述几个关键方面。
概述
加载,链接和初始化是JVM初始进程,其开始把被称为类文件的字节代码加载到JVM中执行。其他进程(如实例化,垃圾收集和完成)会出现在类生命周期的生命中间阶段。最后,卸载过程在生命周期结束时出现。 JVM提供了不同进程执行的环境。只要遵守JVM所理解的标准,什么语言编译器将源代码转换为类文件不是绝对重要的。
除Java之外,还有许多知名的JVM语言,如Clojure,Groovy,Scala,Jruby,Jython等。 程序可以用任何这些语言编写,并由特定语言编译器编译。编译的目标代码以在JVM上运行的方式创建。

虽然JVM对类文件执行指定了一些规则,但以某种低级别视角,它可以修改在底层平台上如何交互,并优化的功能。 这个构思使它成为一种开放的架构,以欢迎特定厂商的调整,并使其在某些情况下更好。
因此,除了Oracle和OpenJDK的JVM实现之外,市场上还有其他可用的实现方案,如CACAO,jikes RVM,Maxine,JamVM等。

加载,链接和初始化过程在将类文件导入JVM的开始启动阶段。虽然其比较复杂,但是如果只讨论这个话题,而无视其他的JVM进程,无法清楚理解的,由于概念相互涉及较密集。例如,如果在加载阶段出现错误,报告将等待直到链接器发挥作用。 因此,加载,链接和初始化的概念可能看起来是离散的,但是它们在许多场合都是重叠的。
类文件介绍

Java类文件的生命周期回顾:Java编译器创建一个类文件作为源代码的文件结果。尽管类文件是二进制数据,但是在没有Java虚拟机(JVM)其是执行不了的。这意味着类文件完全依赖于JVM环境来执行。 JVM提供运行时环境,并理解类文件中所表示的二进制指令。它是与执行类文件指令的基础平台进行交互的JVM。中间人JVM不仅提供了类文件的场地,而且还可作为交换服务和资源的媒介。因此,如果我们分解成功执行类文件所承担的JVM的进程,会发现它们有很多。但是,刚开始JVM在将类文件导入其域的初始阶段中有三个进程。这三个进程被称为加载,链接和初始化。
加载进程
根据Java 8虚拟机规范,它是查找具有特定名称类或接口类型的二进制表示的进程,并从该二进制表示创建类或接口。
JVM提供了两种类加载器。一个被称为引导类加载器,另一个是用户定义的类加载器。引导类加载器在JVM中被严格定义,并根据规范加载类文件。用户定义的类加载器是针对特定供应商实现开放的,可以通过java.lang.Class实例自定义加载类。 请注意(在Java API文档中),该类没有公共构造函数。

因此,Class对象由JVM自动创建,并且可以通过此类成员函数获取有关类的内部数据结构的所有信息。一旦加载了类,JVM将根据内部数据结构进行解析。如果遇到任何问题,即使在加载时间的初始阶段,也就是说,由于类不正确,它不会立即报告问题;相反,它等待直到该类被程序主动引用并报告链接器错误。如果在整个程序过程中没有出现这样的提示,错误可能会持续,但不会报告。
因此,概况地说,加载过程基本上执行这三个功能:
     从类文件创建二进制数据流
     根据内部数据结构解析二进制数据
     创建一个java.lang.Class的实例
链接进程
根据Java 8虚拟机规范,它是采用类或接口并将其组合到JVM的运行时状态的进程,这样可以方便于执行。
链接从类的验证进程开始,确保它遵循语言的语义,并不会破坏JVM的完整性。但是,JVM规范说明了验证进程,并为特定的JVM实现者提供了灵活性,其可以决定何时进行链接活动或如何验证类型。
JVM规定的特定情况异常列表。在这方面,值得一提的是,从二进制数据解析到内部数据结构的一开始就有一些检查和验证,并且此进程的检查确保操作不会崩溃。此外,检查是为了确保二进制数据的结构与其期望的格式一致。加载器还检查的是一个java.lang.Object类的子类,但不包括Object类本身的异常。这通常需要递归加载超级类层次结构。以这种方式,许多验证发生在多个阶段,但通常认为官方验证从链接开始。
一旦验证完成后,JVM将为类变量分配内存,并根据变量的类型将其初始化为默认值。但是,在下一个初始化阶段之前,不会产生实际的初始化(用户定义的初始化值)。这个过程称为准备。最后,在可选的分解阶段,JVM定位在常量池(符号表)中引用的类,接口,域和方法,并从其符号引用中确定具体值。 Java符号引用解决方案再次开放给特定供应商执行。
它可以决定是否在使用的类或接口中解析符号引用,或者在验证过程中解析它们。简而言之,验证检查一个类的二进制陈述在结构上是正确的。并且,它确保必须加载其他类(也许),而不需要验证这些类(如果这些类是Java API库的一部分)。
因此,简而言之,链接过程涉及三个功能:
     验证
     准备
     分解(可选)
初始化进程
根据Java 8虚拟机规范,类或接口的初始化包括执行其类或接口初始化方法。
在类或接口通过验证,准备和可选解析进程进行链接之后,初始化阶段使得该类准备好首先被主动使用。该进程从初始化带有值的类变量开始,其是程序预期的启动。 根据一个大计划,程序员有责任决定类变量的适当值。因此,初始化意味着通过程序员描述的一些初始化例程来初始化类变量,如果类变量尚未被初始化,则初始化类的直接超类。 然而,一个接口的初始化不需要初始化其的超级接口。 这是接口的一个例外。
因此,总结一下,初始化过程涉及以下两个功能:
通过程序员指定的例行初始化类变量。
如果尚未对其初始化,则初始化它的超类。
结论
这只是对JVM中的加载,链接和初始化过程的简要概述。 每个阶段都涉及到更多的复杂性,并被忽略,以保持简单和简洁。