大部份内容都是《深刻理解Java虚拟机上的内容》的总结,少部份内容是来自于网上或者本身的理解。读完应该会把没笔记的markdown文件放在 github上。
本部分笔记对应的是《深刻理解Java虚拟机》第七章,第八章和第九章。java
加载(Loading),验证(Verification),准备(Preparation),解析(Resolution),初始化(Initialization),使用(Using)和卸载(Unloading)7个阶段,其中验证,准备,解析3个阶段称为链接(Linking)。git
加载,验证,准备,初始化和卸载这5个阶段的顺序是必定的。github
文件格式的验证安全
主要目的是保证输入的字节流能正确地解析并存储于方法区以内,格式上符合描述一个Java类型信息的要求。经过这个验证以后,字节流才会进入内存的方法区中存储,因此后面的3个验证阶段都是基于方法区的存储结构进行的。markdown
对字节码描述的信息进行语义分析,保证不存在不符合Java语言规范的元数据信息。数据结构
经过数据流和控制流分析,肯定程序语义是合法的,符合逻辑的。在第二阶段对元数据信息中的数据类型作完校验后,这个阶段对类的方法体进行校验分析。架构
确保解析动做可以正常的进行。模块化
#### 准备 是正式为**类变量**分配内存并设置**类变量**初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这里的初始值“一般状况下”是数据类型的零值。 public static int value = 123 这里value的值是0。 若是类字段的字段属性表中存在ConstantValue,则会在准备阶段就进行赋值 public static final int value = 123 这时value的值是123。 #### 解析 虚拟机将常量池内的符号引用替换为直接引用的过程。 **符号引用(Symbolic Reference)** 符号引用以一组符号来描述所引用的目标,符号能够是任何形式的字面量,只要使用时能无歧义地定位当前目标便可。**符号引用与虚拟机实现的内存布局无关,引用的目标并不必定已经加载到内存中。**各个虚拟机实现的内存布局能够不一样,可是它们能接受的符号引用必须都是一直的,由于符号引用的字面量形式明肯定义在Java虚拟机规范的Class文件中。 **直接引用(Direct Reference)** 直接引用能够是直接指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。直接引用是和虚拟机实现的内存布局有关。若是有了直接引用,那引用的目标并定已经在内存中实现了。 #### 初始化 **类初始化是类加载过程当中的最后一步,前面的类加载过程当中,除了在加载阶段用户应用程序能够经过自定义类加载器参与以外,其他动做彻底是由虚拟机主导和控制的。** 初始化阶段时执行类构造器<clinit>()方法的过程。 <clinit>()是有编译器自动收集类中的全部类变量的赋值动做和静态语句块中的语句合并产生的。 ### 类加载器 对于任意一个类,都须要由加载它的类加载器和这个类自己一同肯定其在Java虚拟机中的惟一性 #### 双亲委派模型 从Java虚拟机的角度来说 * 启动类加载器(Bootstrap ClassLoader)这个类加载器使用C++语言实现,是虚拟机的一部分。 * 全部其余的类加载器,这些类加载器都由Java语言实现,独立于虚拟机外部,并都继承抽象类java.lang.ClassLoader。 从Java开发人员角度分为是分为3种 * 启动类加载器,负责加载<JAVA\_HOME>\\lib目录,或被-Xbootclasspath指定的路径,并被虚拟机识别的类库加载到虚拟机内存中。 * 扩展类加载器(Extension ClassLoader),负责加载<JAVA\_HOME>\\lib\\ext目录中,或者被java.ext.dirs系统变量所指定的路径中的全部类库。 * 应用程序类加载器(Application ClassLoader),又叫系统类加载器,通常状况下这个就是程序中默认的类加载器。 **工做流程** 若是一个类加载器收到了类加载的请求,它首先不会本身去尝试加载这个类,而是把这个请求委派给父类加载器去完成,每一层次的类加载器都是如此,所以全部的加载请求最终都应该传送到顶层的启动类加载器中,只有当父加载器反馈本身没法完成这个加载请求时,子加载器才会尝试本身加载。 虚拟机字节码执行引擎 ---------- ### 运行时栈帧结构 栈帧(Stack Frame)是用于支持虚拟机进行方法调用和方法执行的数据结构,它是虚拟机运行时数据区中的虚拟机栈(Virtual Machine Stack)的栈元素。栈帧存储了方法的局部变量表,操做数栈,动态链接和方法返回地址等信息。每个方法从调用开始至执行完成的过程,都对应着一个栈帧在虚拟机栈里面从入栈道出栈的过程。 只有位于栈顶的栈帧才是有效,称为当前栈帧(Current Stack Frame),与这个栈帧相关联的方法叫作当前方法(Current Mehod)。 #### 局部变量表 局部变量表(Local Varible Table)是一组变量值存储空间,用于存放方法参数和方法内定义的局部变量。在Java程序编译为Class文件时,就在方法的Code属性中肯定了最大容量。 局部变量表的容量以容量槽(Varible Slot)为最小单位。 **因为局部变量表创建在线程的堆栈上,是线程私有的数据,不管读写两个链接的Slot是否为原子操做,都不会一块儿数据安全问题。** 在方法执行时,虚拟机是使用局部变量表完成参数值到参数变量列表的传递过程。先是分配参数,以后再根据方法体内部定义的变量顺序和做用域分配其他的Slot #### 操做数栈 操做数栈(Operand Stack),当方法刚刚开始执行的时候,这个方法的操做数栈是空的,在方法的执行过程当中,会有各类字节码指令往操做数栈中写入和提取数据。 #### 动态链接 每一个栈帧都包含一个指向运行时常量池中该栈帧所属的方法的引用,持有这个引用是为了支持方法调用过程当中的动态链接。 #### 方法返回地址 第一种是执行引擎遇到了任意一个方法返回的字节码指令。这种退出方法称为正常完成出口(Normal Method Invocation Completion) 另外一种,在方法执行遇到了异常,而且这个异常没有在方法体内获得处理。不管是Java虚拟机内部产生的异常,仍是代码中使用athrow字节码指令产生的异常。只要在本方法的异常表中没有搜索到匹配的异常处理器,就会致使方法退出,这种退出方式称为异常完成出口(Abrupt Method Invocation Completion) ### 方法调用 方法调用并不等同于方法执行,方法调用阶段惟一的任务就是肯定被调用方法的版本,暂时还不涉及方法内部的具体运行过程。 #### 解析 字节码中的方法调用指令就一常量池中指向方法的符号引用做为参数。这些符号引用一部分会在类加载阶段或者第一次使用的时候就转换为直接引用,这种转化称为**静态解析**。另一部分将在每一次运行期间转化为直接引用,这部分称为**动态链接**。 在类加载阶段,会将其中的一部分符号引用,转化为直接引用,这种解析能成立的前提是:方法在程序真正运行以前就有一个可肯定的目标,而且这个方法调用版本在运行期不可修改。主要包括静态方法和私有方法两大类。 **非虚方法** 只要能被invokestatic和invokespecial指令调用的方法,均可以在解析阶段中肯定惟一的调用版本,符合这个条件的有静态方法,私有方法,实例构造器,父类方法,它们在类加载的时候就会把符号引用解析为该方法的直接引用。这些方法称为非虚方法。 final方法也是一种非虚方法。 ### 分派 #### 静态分派 全部依赖静态类型来定位执行版本的分派动做,称为静态分派。静态分派的典型应用是**方法重载**。静态分派发生在**编译阶段**,**所以肯定静态分派的动做实际上不是由虚拟机执行。**有时自变量不须要定义,因此字面量没有显示的静态类型,它的静态类型只能经过语言上的规则区理解和推断。 #### 动态分派 动态分派和**重写**有着密切关系。 **实现** 为类在方法区中创建一个虚方法表(Virtual Mehtod Table,在invokeinterface执行时也会用到接口方法表Inteface Method Table)。虚方法表中存放着各个方法的实际入口地址。 **方法表通常在类加载的链接阶段进行初始化,准备了类的变量初始值后,虚拟机会把该类的方法表也初始化完成。** #### 单分派和多分派 方法的接收者与方法的参数统称为方法的宗量。根据分派基于多少种宗量,能够分派划分为单分派和多分派。 单分派是根据一个宗量对目标方法进行选择,多分派是根据多于一个宗量对目标方法进行选择。 #### 动态类型支持 java.lang.invoke ### 基于栈的字节码解释执行引擎 Java编译器输出的指令流,基本上是一种基于栈架构的指令集架构(Instruction Set Architecture IA),指令流中的指令大部分都是基于零地址指令,他们依赖于操做数栈进行工做的。 基于栈的指令集主要优势就是可移植,寄存器由硬件直接提供,程序直接依赖这些硬件寄存器则不可避免的受到硬件约束。主要缺点,执行速度相对会稍慢一点。 类加载及执行子系统的案例与实战 --------------- ### Tomcat 在Tomcat目录结构中,有3组目录(“/common/\*”,“/server/\*”和“/shared/\*”)能够存放Java类库,另外还能够加上Web应用程序自身的目录“WEB-INF” 放置在/common目录中:类库能够被Tomcat和全部的Web应用程序共同使用 放置上/server目录中:类库能够被Tomcat使用,对全部的Web应用程序均可不见 放置在/shared目录中:类库可被全部的Web应用程序共同使用,但对Tomcat本身不可见 放置在/WebApp/WEB-INF目录中:类库仅仅被此Web应用程序使用,对Tomcat和其余Web应用程序都不可见 ### OSGI 一种动态模块化规范