《java虚拟机》----类加载机制

时间:2023-01-03 11:11:29

No1:

实现语言无关性的基础仍然是虚拟机和字节码存储格式,虚拟机只与Class文件这种特定的二进制文件格式所关联,并不关心Class的来源是何种语言。

No2:

Class文件是一组以8位字节为基础单位的二进制流,整个Class文件本质上就是一张表

No3:

常量池可以理解为Class文件中的资源仓库,它是Class文件结构中与其他项目关联最多的数据类型,也是占用Class文件空间最大的数据项目之一,同时它还是在Class文件中第一个出现的表数据项目。、

常量池中主要存放两大类常量:字面量和符号引用;

字面量比较接近于java语言层面的常量概念,如文本字符串、声明为final的常量值等。

符号引用则属于编译原理方面的概念,包括下面三类常量:

1.类和接口的全限定名

2.字段的名称和描述符

3.方法的名称和描述符

No4:

虚拟机类加载机制:虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始化,最终形成可以被虚拟机直接使用的java类型。

No5:

在java语言里面,类型的加载、连接和初始化过程都是在程序运行期间完成的,这种策略虽然会令类加载时稍微增加一些性能开销,但是会为java应用程序提供高度的灵活性,java里天生可以动态扩展的语言特性就是依赖运行期间动态加载和动态连接这个特点实现的。

No6:

类从被加载到虚拟机内存中开始,到卸载出内存为止,整个生命周期包括:

加载(Loading)--验证(Verification)--准备(Preparation)--解析(Resolution)--初始化(Initialization)--使用(Using)--卸载(Unloading)

其中验证、准备、解析3部分统称为连接(Linking)

No7:

虚拟机有且只有5种情况必须立即对类进行初始化:

1.遇到new、getstatic、putstatic或invokestatic这4条字节码指令时(最常见场景是,使用new关键字实例化对象、读取或设置一个类的静态字段、调用一个雷的静态方法)

2.使用java.lang.reflect包对类进行反射调用

3.如果一个父类没有初始化的时候

4.虚拟机启动,用户需要指定一个要执行的主类

5.一个java.lang.invoke.methodhandle实例最后解析结果REF_getStatic、REF_putStatic、REF_invokeStatic的方法句柄

No8:

加载阶段,虚拟机需要完成3件事:

1.通过一个类的全限定名来获取定义此类的二进制字节流

2.将这个字节流锁代表的静态存储结构转化为方法区的运行时数据结构

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

No9:

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

包括4个动作:

1.文件格式验证(验证字节流是否符合Class文件格式的规范)

(1)是否以默数0xCAFEBABE开头

(2)主、次版本号是否在当前虚拟机处理范围以内

(3)常量池的常量中是否有不被支持的常量类型(检查常量tag标志)

(4)指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量

(5)CONSTANT_Utf8_info型的常量中是否有不符合UTF8编码的数据

(6)Class文件中各个部分及文件本身是否有被删除的货附加的其他信息

2.元数据验证(保证字节码描述的信息符合java语言规范)

(1)这个类是否有父类

(2)这个类的父类是否继承了不允许被继承的类(final类)

(3)这个类不是抽象类,是否实现了其父类或接口之中要求实现的所有方法

(4)类中的字段、方法是否与父类产生矛盾

3.字节码验证(目的是通过数据流和控制流分析,确定程序语义是合法的、符合逻辑的)

(1)保证任意时刻操作数栈的数据类型与指令代码序列都能配合工作,不会出现类似int变long类型

(2)保证跳转指令不会跳转到方法体以外的字节码指令上

(3)保证方法体中的类型转换是有效的

4.符号引用验证

(1)符号引用中通过字符串描述的全限定名是否能找到对应的类

(2)在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段

(3)符号引用中的类、字段、方法的访问性(private、protected、public、default)是否可被当前类访问

No10:

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段,这些变量所使用的内存都将在方法区中进行分配。这时候进行内存分配的仅包括类变量(被static修饰的变量),而不包括实例变量,实例变量将会在对象实例化时随着对象一起分配在java堆中。

No11:

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

符号引用(Symbolic References):用一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义的定位到目标即可。与虚拟机实现的内存布局无关,引用的目标不一定已经加载到内存中。

直接引用(Direct Reference):直接引用可以是直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。与虚拟机实现的内存布局相关的。如果有了直接引用,那引用的目标必定已经在内存中存在。

No12:

1.类或接口的解析  2.字段解析  3.类方法解析  4.接口方法解析

No13:

初始化阶段是执行类构造器<clinit>()方法的过程

No14:

类加载器:虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到java虚拟机外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码模块称为“类加载器”;

No15:

比较两个雷是否“相等”,只有在这两个类是由同一个类加载器加载的前提下才有意义,否则,即使这两个类来源于同一个Class文件,被同一个虚拟机加载,只要加载它们的类加载器不同,那这两个类就必定不相同。

这里所指的“相等”,包括代表类的Class对象的equals()方法、isAssignableFrom()方法、isInstance()方法的返回结果,也包括使用instanceof关键字做对象所属判定等情况。

No16:

系统提供的类加载器:

1.启动类加载器(Bootstrap ClassLoader)

2.扩展类加载器(Extension Classloader)

3.应用程序类加载器(Application ClassLoader)

No17:

双亲委派模型

《java虚拟机》----类加载机制

工作过程:类加载器收到类加载的请求,先把请求委派给父类加载器去完成,层层至顶,只有当父加载器反馈自己无法完成这个请求时,子加载器才会尝试自己去加载。

保证了java中已经存在的类会被先执行,而不去执行程序员的重名类。

No18:

面试:描述JVM加载class文件的原理机制

* JVM中类的装载是由类加载器(ClassLoader)和它的子类来实现的,Java中的类加载器是一个重要的Java运行时系统组件,它负责在运行时查找和装入类文件中的类

* 由于Java的跨平台性,经过编译的Java源程序并不是一个可执行程序,而是一个或多个类文件。当Java程序需要使用某个类时,JVM会确保这个类已经被加载、链接(验证、准备和解析)和初始化。类的加载是指把类的.class文件中的数据读入到内存中,通常是创建一个字节数组读入.class文件,然后产生与所加载类的Class对象。加载完成后,Class对象还不完整,所以此时的类还不可用。当类被加载后就进入连接阶段,这一阶段包括验证、准备(为静态变量分配内存并设置默认的初始值)和解析(将符号引用替换为直接引用)三个步骤。最后JVM对类进行初始化,包括

1)如果类存在直接的父类并且这个类还没有被初始化,那么就先初始化父类

2)如果类中存在初始化语句,就 依次执行这些初始化语句

3)类的加载是由类加载器完成的,类加载器包括:根加载器(BootStrap)、扩展加载器(Extension)、系统加载器(System)和用户自定义类加载器(java.lang.ClassLoader的子类)

* 从Java2(JDK1.2)开始,类加载过程采取了父类委托机制(PDM)PDM更好的保证了java平台的安全性,在该机制中,JVM自带的Bootstrap是根加载器,其他的加载器都有且仅有一个父类加载器。类的加载首先请求父类加载器加载,父类加载器无能为力时才由其子类加载器自行加载

* JVM不会向java程序提供对Bootstrap的引用。下面是关于几个类加载器的说明

1)Bootstrap:一般用本地代码实现,负责加载JVM基础核心类库(rt.jar)

2)Extension:从java.ext.dirs系统属性所指定的目录中加载类库,它的父加载器是Bootstrap

3)System:又叫应用类加载器,其父类是Extension。它是应用最广泛的类加载器。它从环境变量classpath或者系统属性java.class.path所指定的目录中记载类,是用户自定义加载的默认父加载器。