虚拟机类加载机制------类加载的过程

1.加载

虚拟机需要干三件事:

①、通过一个类的的全限定名来获取定义此类的二进制字节流(没有规定二进制字节流从那里获取,怎样获取,许多java技术也都建立在这基础上)

②将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构(将常量池转变成运行时常量池)

③在内存中生成一个代表这个类的java.lang.Class对象,作为方法区着各类的各种数据的访问入口。

相比较于类加载过程的其他阶段,非数组类获取类的二进制字节流的动作是开发人员可控性最强的,因为加载阶段既可以使用系统提供的引导类加载器来完成,也可以由用户自定义的类加载器去完成

,开发人员可以自己重写一个类加载器的loadClass()方法

对于数组类,不通过类加载器创建,由java虚拟机直接创建的。但是数组类的元素类型(去掉所有维度的类型)最终是要通过类加载器去创建。

数组类的创建过程要遵循以下原则:

①、如果数组的组件类型(数组去掉一个维度的类型)是引用类型,就递归家在过程去加载这个组件类型,数组C将在加载该组件类型的类加载器的类名称空间上被标识。

②、如果数组的组件类型不是引用类型(例如int[]数组),java虚拟机将会把数组C标记为与引导类加载器关联

③、数组类的可见性与它的组件类型的可见性一致,如果组件类型不是引用类型,那数组类的可见性将默认为public

加载阶段完成后,虚拟机外部的二进制字节流就按照虚拟机所需的格式存储在方法区之中,方法区中的数据存储格式由虚拟机自行定义。然后在内存中实例化一个java.lang.Classleide duix (没有明确规定在java堆中吗、,对于Hotspot来说,Class对象比较特殊,存放在方法区里面)。这个对象作为程序访问方法区中这些类型数据的外部接口。

连接阶段:

2 .验证

验证的目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的安全。

整体上看,验证阶段大致上会完成下面四个阶段的检验工作:

(1)基于字节流检验

文件格式验证:基于二进制字节流进行的,只有经过这个阶段的验证,字节流才会进入内存的方法区中存储。目的是保证输入的字节流能正确解析并存储于方法区之内

(2)基于方法区的存储结构

元数据验证:对字节码描述的信息进行语义分析,保证不存在不符合JAVA语言规范的元数据信息。

字节码验证:最复杂的一个阶段,对类的方法体进行校验,保证被叫严磊的方法在运行时不会做出还虚拟机的事。目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的。

符号引用验证:发生在虚拟机将符号引用转化为直接引用的时候,这个转化动作将在解析阶段中发生。目的是确保解析动作能正常执行。

3.准备

准备阶段是正式为类变量分配内存并设置初始值的阶段,这些变量所使用的内存都在方法区中进行分配。

注意两点:一点是类变量,另一点是初始值“通常情况下”是数据类型的零值。例如“public static int value=123”在准备过程value的值为0而不是123,因为这时候尚未开始执行任何java方法,而把value赋值为123的puststatic指令是程序被变异后,存放于类构造器<clint>()方法之中,所以把value赋值为123的动作在初始化时候才会进行

特殊:如果类字段的字段属性表中存在ConstacntValue属性,那么准备阶段变量value就会被初始化为所指定的值

public static final int value=123;

这是value被赋值为123.

4.解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。

符号引用和直接引用又有什么关联呢?

符号引用:符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时可以无歧义的定位到目标即可。符号引用与虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中,各种虚拟机实现的内存布局可以各不相同,但是它们能接收的符号引用必须都是一致的,因为符号引用的字面量形式明确定义在java虚拟机规范的Class文件格式中。

直接引用:直接引用可以是直接指向目标的指针、相对偏移量或是一个嫩详见定位到目标的句柄。直接引用时和虚拟机实现的内存布局相关的,通过一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。如果有了直接引用,被引用的目标必在内存中存在。

解析的具体时间没有规定,只要求了在执行anewarray  checkcast getfield getstatic instanceof invokedynamic invokeinterface invokespecial invokestatic invokevirtual ldc multianewarray new putfield putstatic 这十六个用于操作符号引用的字节码指令之前,先对他们所使用的符号引用进行解析

对一个符号引用进行多次解析请求,除了invokedynamic指令以外,虚拟机可以缓存第一次解析的结果,之后再请求解析,可以直接调用避免重复进行。

invokedynamic指令是“动态调用点限定符”动态也就是必须等到程序实际运行到这条指令的时候,解析才进行的。

解析动作主要针对

类或接口解析 :

假设当前代码所处的类为D,如果要把一个从未解析过的符号引用N解析为一个类或接口C的直接引用,那虚拟机完成这个解析的过程需要以下3个步骤

①:C不是一个数组类型,虚拟机会把代表N的全限定名传递给D的类加载器去加载这个类C。在加载过程中,由于元数据验证、字节码验证的需要,有可能触发其他相关类的加载动作。

②:如果C是一个数组类型,并且数组的元素类型为对象,例如N是“[Ljava/lang/Integer”的形式,那将会按照第1点的规则加载数组元素类型,如果N的描述符是前面那样,需要加载的元素类型就是“java.lang,Integer”,接着由虚拟机生一个代表此数组维度和元素的数组对象。

③:如果上面的步骤没有出现异常,C已经在虚拟机中成为了一个有效的类和接口了,但是解析完成之前还有进行符号引用验证,确保D是否具备对C的访问权限。

字段解析:

要解析字段符号引用,首先要对字段表内字段所属的类或接口的符号引用进行解析,如果解析成功,那这个字段所属的类或接口用C表示,虚拟机规范要求安好如下步骤对C进行后续字段的搜索

①:如果C本身就包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束

②:否则如果在C中实现了接口,将会按照继承关系从下往上递归搜索各个接口和它的父接口,如果接口中包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束

③:否则,如果C不是java.lang.Object的话,将会按照继承关系从下往上递归搜索其父类,如果父类包含了简单名称和字段描述符都与目标相匹配的字段,则返回这个字段的直接引用,查找结束

④:否则查找失败,抛出异常java.lang.NoSuchMethodError

⑤::如果上面的步骤没有出现异常,但是解析完成之前还有进行符号引用验证,确保是否具备对字段的访问权限。

public class FieldResolution{

  interface Interface0{
     intA=1;
  }
  interface Interface1 extends Interface0{

  int A=2;
}
static class Parent implements Interface1{
    public static int A=3;
}
public static void main(Strings[] args){
  System.out.println(Parent.A);

}

}

如果一个字段出现在C的接口和父类中,或者同时在自己或父类的多个接口中出现,那编译器将可能拒绝编译

类方法解析:

要解析类方法符号引用,首先要对类方法表中方法所属的类或接口的符号引用进行解析,如果解析成功,那这个方法所属的类或接口用C表示,虚拟机规范要求安好如下步骤对C进行后续类方法的搜索

①:类方法和接口房符号引用的常量类型定义是分开的,如果在类方法表中发现class_index中索引的C是个接口,直接抛出java.lang.IncompatibleClassChangeError异常

②:如果C本身就包含了简单名称和描述符都与目标相匹配的方法,则返回这个方法的直接引用,查找结束

③:否则,在C的父类中递归查找,如果父类包含了简单名称和描述符都与目标相匹配的方法,则返回这个方法的直接引用,查找结束

④:在类C实现的接口列表及它们的父接口之中递归查找是否有简单名称和描述符都与目标相匹配的方法, 如果存在匹配的方法,说明类C是一个抽象类,这时查找结束,抛出java.lang,AbstractMethodError异常

④:否则查找失败,抛出异常java.lang.NoSuchMethodError

⑤::如果上面的步骤没有出现异常,但是解析完成之前还有进行符号引用验证,确保是否具备对方法的访问权限。

接口方法解析:

方法类型解析:

方法句柄解析:

调用点限定符解析:

7类符号引用进行解析

时间: 2024-10-15 19:32:51

虚拟机类加载机制------类加载的过程的相关文章

Jvm(55),虚拟机类加载机制----类加载的过程----解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,符号引用在前一章讲解 Class文件格式的时候已经出现过多次,在Class文件中它以CONSTANT_Class_info. CONSTANT_Fieldref_info.CONSTANT_Methodref_info等类型的常量出现,那解析阶段中所说的直接引用与符号引用又有什么关联呢? 符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可

虚拟机类加载机制——类加载时机

由于道行不够深,所以此篇类加载机制的讲解主要来自于<深入理解Java虚拟机——JVM高级特性与最佳实践>的第7章 虚拟机类加载机制. 在前面<初识Java反射>中我们在开头提到要了解Java反射,就得要了解虚拟机的类加载机制.在这里,我们来试着窥探一下何为类加载. “虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,类型的加载.连接和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制.”这句话确实读着好懂,但到底类加载做了什么呢?我们都知道

Jvm(56),虚拟机类加载机制----类加载的过程----初始化

类初始化阶段是类加载过程的最后一步,前面的类加载过程中,除了在加载阶段用户应用程序可以通过自定义类加载器参与之外,其余动作完全由虚拟机主导和控制.到了初始化阶段,才真正开始执行类中定义的Java程序代码(或者说是字节码). 在准备阶段,变量已经赋过一次系统要求的初始值,而在初始化阶段,则根据程序员通过程序制定的主观计划去初始化类变量和其他资源,或者可以从另外一个角度来表达:初始化阶段是 执行类构造器<clinit>()方法的过程.我们在下文会讲解<clinit>()方法是怎么生成的

JVM类加载机制---类加载的过程

一.类加载的时机 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用.卸载 7个阶段,其中验证.准备.解析 3个部分统称为 连接. 二.具体步骤解析 1.加载 加载阶段,虚拟机要完成以下3件事情: 1)通过一个类的全限定名来获取定义此类的二进制字节流: 2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构: 3)在内存中生成一个代表这个类的java.lang.class对象,作为方法区这个类的各种数据的访问入口. 相对于类加载过

虚拟机类加载机制--类加载器

准备阶段的"通过一个类的全限定名来获取描述此类的二进制字节流"这个动作放到了Java虚拟机外部去实现,以便让应用程序自己决定如何如获取所需要的类.实现这个动作的代码模块称为"类加载器" 1.类与类加载器 每一个类加载器都有一个独立的类名称空间,由类加载器和类一起合作才能确定一个类在虚拟机中的唯一性.也就是说:比较两个类是否"相等",即使他们来自同一个Class文件,在同一个虚拟机上被加载,如果加载它们的类加载器不同,那么这两个类就不相等. 这里的

Jvm(51),虚拟机类加载机制----类加载的时机

在了解下面的举的例子之前我们先来了解一下类的加载顺序? 1 public class test1 { 2 public static void main(String[] args) { 3 C c = new C(); 4 } 5 } 6 7 class A{ 8 int a = 0; 9 Method m = new Method(a); 10 static int a1 = 10; 11 static{ 12 System.out.println("A:执行静态代码块A"+a1)

Java虚拟机 - 类加载机制

[深入Java虚拟机]之四:类加载机制 类加载过程     类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载七个阶段.它们开始的顺序如下图所示: 其中类加载的过程包括了加载.验证.准备.解析.初始化五个阶段.在这五个阶段中,加载.验证.准备和初始化这四个阶段发生的顺序是确定的,而解析阶段则不一定,它在某些情况下可以在初始化阶段之后开始,这是为了支持Java语言的运行时绑定(也成为动态绑定或晚期绑定).另外注意这里的几个阶段是按顺序开

Java 虚拟机程序执行:02 虚拟机的类加载机制

虚拟机的类加载机制 虚拟机的类加载机制 类加载的时机 类的显式加载和隐式加载 类加载的过程 类的生命周期 加载 加载的 3 个阶段 分类 验证 准备 解析 初始化 类加载器 如何判断两个类 “相等” 类加载器的分类 双亲委派模型 类加载的时机 JVM 会在程序第一次主动引用类的时候,加载该类,被动引用时并不会引发类加载的操作.也就是说,JVM 并不是在一开始就把一个程序就所有的类都加载到内存中,而是到不得不用的时候才把它加载进来,而且只加载一次.那么什么是主动引用,什么是被动引用呢? 主动引用

两道面试题,带你透彻解析Java类加载机制

在许多Java面试中,我们经常会看到关于Java类加载机制的考察,例如下面这道题: class Grandpa { static { System.out.println("爷爷在静态代码块"); } } class Father extends Grandpa { static { System.out.println("爸爸在静态代码块"); } public static int factor = 25; public Father() { System.ou