小王深入理解Java虚拟机学习笔记(四)

第七章 虚拟机类加载机制

类加载的缘由

计算机的主要存储部件是内存和磁盘。程序是存储在磁盘里面的,程序要运行则必须加载到内存中。java程序首先会被JVM编译成class文件,之后再把class文件加载到内存中。

何时类加载

我们首先要清楚的是,Java 类何时会被加载?《深入理解 Java 虚拟机》给出的答案是:
(1)使用new实例化对象的时候;读取或配置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候;调用一个类的静态方法的时候。

(2)使用java.lang.reflect包的方法对类进行反射调用的时候。如果类没有进行过初始化,则需要先触发其初始化。

(3)当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。

(4)当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的类),虚拟机会先初始化这个主类
简而言之,程序过程中需要这个类就会被加载。

类加载过程

JVM中的类加载过程分为五步,加载、验证、准备、解析和初始化。
图片.png

1、加载

在加载阶段,虚拟机需要完成的事情如下:
(1) 通过一个类的全限定名来获取定义此类的二进制字节流。
(2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
(3)在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

2、验证

验证的目的是确保Class文件的字节流中包含的信息符合当前虚拟机的要求,不会危害虚拟机自身的安全。
其分为4个步骤:文件格式验证,元数据验证,字节码验证,符号引用验证。其中文件格式验证是直接对字节流进行操作的,其余3项是在方法区中进行的。

3、准备

此阶段是正式为类变量分配内存并设置类变量初始值的阶段。其是在方法区中进行分配的。有两个注意点:

(1)此时只是对类变量(被static修饰的变量)进行内存分配,而不是对象变量。给对象分配内存是在对象实例化时,随着对象一起分配到java堆中。

(2)如果一个类变量没有被final修饰,则其初始值是数据类型的零值。比如int类型的是0,boolean类型的是false。举个例子来说明:

public static int value=123;

在准备阶段过后的初始值为0而不是123,因为这个时候尚未开始执行任何java方法,而把value赋值为123的putstatic指令是程序被编译后,存放于类构造器< clinit >()方法之中。所以把value赋值为123的动作将在初始化阶段才会执行。

public static final int value=123;

此时因为有final,所以在准备阶段value就已经被赋值为123了。

4、解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。可对类或接口、字段、类方法、接口方法等进行解析。

符号引用是什么:

符号引用就是包含类的信息,方法名,方法参数等信息的字符串,它供实际使用时在该类的方法表中找到对应的方法。

直接引用是什么:

直接引用就是偏移量,通过偏移量可以直接在该类的内存区域中找到方法字节码的起始位置。
符号引用是告诉你此方法的一些特征,你需要通过这些特征去寻找对应的方法。直接引用就是直接告诉你此方法在哪。

5、初始化

此阶段用于初始化类变量和其它资源,是执行类构造器< clinit >()方法的过程,此时才是真正开始执行类中定义的java程序代码。

类和类加载器(ClassLoader)

在类的加载阶段,需要通过一个类的全限定类名来获取描述该类的二进制字节流,这个动作需要放到JVM外部去实现,以便让应用程序自己决定如何去获取所需要的类。实现这个动作的代码称为类加载器。

对于任何一个类来说,都必须由加载它的类加载器和这个类本身一起共同确定这个类在JVM中的唯一性。因此,需要比较两个类是否相等,必须满足这个两个类是由同一个类加载器加载的前提。这个感觉和equal方法比较类似,equals方法要比较两个对象是否相等,则这两个对象的hashcode()值必须相等。

双亲委派模型

双亲委派机制是类加载所采取的一种方式。如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成。每一层的类加载器均是如此。只有当父加载器反馈自己无法完成这个请求时,子加载器才会尝试自己去加载。
使用双亲委派模型来组织类加载器之间的关系,有一个显而易见的好处就是java类随着它的类加载器一起具备了一种带有优先级的层次关系。
JVM中的类加载器如下:
图片.png

类比到现实:小明想买一个玩具挖土机,可他又不好意思直接张口说。所以,发生了下面的对话。

小明去问他爸爸:爸爸你有挖土机吗?
爸爸说:没有哎
接着爸爸问爷爷:爸爸爸爸,你有挖土机吗?
爷爷说:没有哎
接着爷爷问太爷爷:爸爸爸爸,你有挖土机吗?
太爷爷说:我也没有。让重孙子去买一个吧。
结果小明就高高兴兴地自己去买了一个玩具挖土机。

分类
启动类加载器是使用C++实现的,是虚拟机自身的一部分。
其它类加载器是由java语言实现的,独立于虚拟机外部,并且全都继承自抽象类java.lang.ClassLoader。
好处
以String类为例。就算是用户自己写了一个String类的实现,那对此类进行加载时,也只会委派给启动类加载器来对JDK中原本的String类进行加载,而自定义的String类永远不会被调用。这样保证了系统的安全。

参考文献:

[1]https://www.php.cn/java-article-429354.html
[2]深入理解Java虚拟机第三版
[3]https://www.helloxinyue.com/archives/%E5%AD%A6%E4%B9%A0jvm%E6%80%BB%E7%BB%93%E4%B8%89

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值