深入理解Java虚拟机读书笔记--第七章 虚拟机类加载机制

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/xiaoyaohuqijun/article/details/78072268

第七章 虚拟机类加载机制

 

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

 

这里的类包括了类和接口的可能性,Class文件指一串二进制的字节流,无论以何种形式存在。

 

类加载是在程序运行期间完成的。

 

类的生命周期包括加载,验证,准备,解析,初始化,使用和协作7个阶段

 


JAVA 虚拟机规范严格对丁了有且仅有5种情况必须立即对类进行“初始化”(而加载,验证,准备自然要在之前开始):

1.      遇到new, getstatic,putstatic,invokestatic 4 条字节码指令时,  如果类没有初始化,则需要先触发其初始化。

即使用new 实例化对象,  读取或设置一个类的静态字段(final static 除外, 编译器已放入常量池),调用类的静态方法

2.       使用java.lang.reflect包的方法对类进行反射调用时;

3.      初始化一个类, 若其父类没有初始化,则要先初始化其父类;

4.      虚拟机启动时,先初始化要执行的主类(包含main()方法的那个类);

5.      当使用jdk 1.7 的动态语言支持是, 如果一个java.lang.invoke.MethodHandle实例最后解析的结果为REF_getStatic,REF_putStatic,REF_invokeStatic的方法句柄,并且这个方法句柄对应的类没有初始化,则需要先触发其初始化

 

接口与类有一个区别:  接口初始化, 不要求其父接口完成了初始化, 只有在真正使用到父接口的时候才会初始化。

 

 

类加载具体过程

1.      加载(Loading)

一, 通过类的全限定名获取定义此类的二进制字节流

二, 将字节流代表的静态存储结构转化为方法区的运行时数据结构

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

 

2.      验证(Verification)

一, 文件格式验证: 字节流是否符合Class文件规范: 是否以魔数0xCAFEBABE开头,主次版本号是否在当前虚拟机处理范围之类等等,等等

二, 元数据验证

对字节码描述信息进行语义分析,保证类的元数据信息符合Java规范

三, 字节码验证

通过数据流和控制流分析,确定程序的语义是合法的(对类的方法体进行校验分析)

四, 符号引用验证

发生在解析阶段,验证符号引用的正确性

 

3.      准备(Preparation)

类变量分配内存并设置类变量初始值, 这里的初始值通常是零值(不是代码中的初始值,代码还没执行)

如果是static final, 则初始值为常量值。

 

4.      解析(Resulution)

符号引用替换为直接引用的过程

 

5.      初始化(Initailization)

执行类构造器<clinit>()方法的过程   (这里才执行Java 代码了, 类变量的值才变成代码中定义的值)

 

类构造器<clinit>()由编译器收集类中所有类变量的赋值动作静态语句块中的语句合并而成,顺序由源文件中出现的顺序决定

 

加载阶段是开发人员可控性最强的,开发人员可以通过定义自己的类加载器去控制字节流的获取方式

数组类不由类加载器创建,由Java 虚拟机直接创建,但数组类的元素类型还是由类加载器去创建的。

Class对象比较特殊,虽然是对象,存放在方法区中,作为程序访问方法区中类型数据的入口。

 

符号引用: 符号引用以一组符号来描述所有引用的目标,符号可以使任何形式的字面量,  与虚拟机的内存布局无关,引用的目标不一定已经加载到内存中。

直接引用:直接执行目标的指针,相对偏移量或者一个能间接定位到目标的句柄。 直接引用和虚拟机实现的内存布局相关的, 直接引用的目标肯定已存在内存中。

 

类加载器:  通过一个类的全限定名来获取描述此类的二进制流

 

类唯一性

对任何一个类, 都需要由加载它的类加载器和这个类本身一同确立其在Java 虚拟机中的唯一性,  因为每一个类加载器都拥有一个独立的类名称空间。

 

类加载器分类

1.      启动类加载器(BootstrapClassLoader) :

负责加载 <JAVA_HOME>\lib 目录或者-Xbootclasspath参数指定的路径下的类库

 

2.      扩展类加载器(ExtensionClassLoader)

负责加载<JAVA_HOME>\lib\ext 目录中的或者被java.ext.dirs系统变量指定路径下的类库

 

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

加载用户类路径(ClassPath) 上所指定的类型,  一般情况下程序中默认的类加载器

 

 

 

 

类加载器双亲委派模型(Parents Delegation Model):

 


双亲委派模型是Java设计者推荐的一种类加载器实现方式。

要求除了顶层的启动类加载器外, 其余的类加载器都要自己的父类加载器,  父子关系使用组合来复用父类加载器的代码。

 

双亲委派模型工作过程

如果一个类加载器收到了类加载的请求, 首先把这个请求委托给父类加载器去完成,  每一层的类加载器都是如果,因此所有请求最终都会传送到启动加载器中。只有当父类加载器反馈无法完成该请求(它的搜索范围中没有找到需要的类),子加载器才会尝试自己去加载。

 

 

双亲委派模型的好处

Java 类随着它的类加载器一起具备了一种带有优先级的层次关系,保证类在各种不同的类加载器环境中都是同一个类。

类的路径固定,不论使用什么加载器最终都是同一个类加载器, 例如java.lang.Object, 它放在rt.jar中,  由于双亲委派模型, 不论使用什么类加载器,  最终Object 都是由启动类加载器加载的,  因此Object 在各种类加载器环境中都是同一个类(PS. 记住类加载器和类一同确立类的唯一性)。

没有更多推荐了,返回首页