虚拟机类加载机制(深入理解java虚拟机学习笔记)

类的生命周期:

加载,验证,准备,解析,初始化,使用,卸载。其中验证,准备,解析3个部分统称为连接。

加载,验证,准备,初始化和卸载这5个阶段的顺序是确定的,类的加载过程必须按照这种顺序按部就班的开始。

 

在且只在以下5种场景,必须对类进行初始化操作:

1)使用new关键字实例化对象的时候,读取或设置一个类的静态字段时,以及调用一个类的静态方法时。

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

3)初始化一个子类,但其父类未初始化时。

4)虚拟机启动时的主类,即包含main()方法的类。

5)使用1.7的动态语言支持//TODO

 

类加载的过程:

1)加载

加载指的是以下三件事情:

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

<2> 将字节流中数据转换为方法区的运行时数据结构。

<3> 在内存中生成一个java.lang.Class 对象,作为这个类的方法区数据的访问入口(接口)。

2)验证

这一阶段的目的是为了确保Class 文件的字节流中包含的信息符合虚拟机的要求,并且不会危害虚拟机的安全。

原因:Class文件不一定由编译而来,可能出现问题。包括:

<1> 文件格式验证

目的是保证字节流能正确的解析并存入方法区。部分验证点如下:

主次版本号是否在当前虚拟机的处理范围。

常量池中各常量tag标志是否正常。

指向常量池的各索引值是否正确。

......

<2> 元数据验证

目的是保证字节码描述的语义信息符合JAVA语言规范,如:

是否有父类(除了object类,所有类都要有父类)

是否继承了不允许被继承的类

字段是否与父类矛盾(如覆盖了父类的final字段)

......

<3> 字节码验证

目的是通过数据流和控制流分析,保证程序语义是合法的,符合逻辑的。如保证跳转指令不会跳转到方法体以外的字节码指令上。

<4> 符号引用验证

该验证发生在虚拟机将符号引用转换为直接引用时,将在解析阶段发生。包括:

符号引用中通过字符串描述的全限定名是否能找到对应的类(父类初始化与子类之前)

指定类中是否有符合方法/字段描述符以及简单名称的方法和字段。

符号引用的类/字段/方法的访问性。

 

此外,验证阶段可以通过-Xverify:none关闭。

 

3)准备

此阶段为类变量分配内存并设置变量初始值,该初始值指的是数据类型的零值。

4)解析

此阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。CONSTANT_Fieldref_info只包含其他类的字段,本类中定义的字段不会被此描述。

<1> 字段解析

首先解析字段所属类或接口C,然后按以下顺序查找:C本身,C接口,C父类。

<2> 类方法解析

首先解析方法所属类C,然后按以下顺序查找:C本身,C父类,C接口(此时说明C是抽象类,抛出异常)。

<3> 接口方法解析

首先解析方法所属接口C,然后按以下顺序查找:C本身,C父接口。

5)初始化

将执行<clinit>()方法。

 

类加载器

一个类由该类的类加载器与类本身唯一确定。

bootstrap classloader:加载<JAVA_HOME>\bin目录下的,或被-Xbootclasspath参数指定的路径下的,并被虚拟机识别的类库。

extension classloader:加载<JAVA_HOME>\lib\ext目录下的,或被java.ext.dirs系统变量所指定的路径下的所有类库。

application classloader:加载用户类路径上所指定的类库(classpath环境变量指定的路径)。

 

双亲委派模式:

如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给自己的父亲加载器去完成。

好处:java类也具备了一种带有优先级的层次关系。解决了基础类的统一问题。

posted @ 2019-08-17 23:00  zpncepu  阅读(98)  评论(0编辑  收藏  举报