JVM-类加载

类加载的过程: Loading(装载)阶段 - Linking(链接)阶段 - Initialization(初始化)阶段 - using - unloading

Loading(装载)阶段

所谓装载,简而言之就是将Java类的字节码文件加载到JVM虚拟机内存中,并在内存中构建出Java类的原型--类模板对象(类元信息)。
装载阶段,简言之,查找并加载类的二进制数据,生成Class的实例。
在加载类时,Java虚拟机必须完成以下3件事情:
1、通过类的全限定名,获取类的二进制数据流。
2、解析类的二进制数据流为方法区内的数据结构(Java类模型)
3、创建java.lang.Class类的实例,表示该类型。作为方法区这个类的各种数据的访问入口


Linking(链接)阶段分为验证 - 准备 - 解析

验证阶段: 它的目的是保证加载的字节码是合法、合理并符合规范的

准备阶段: 为类的静态变量分配内存, 并设置默认初始值

1.这里不包含基本数据类型的字段用static final修饰的情况,因为final在编译的时候就会分配了,准备阶段会显式赋值
2.注意这里不会为实例变量分配初始化,实例变量是会随着对象一起分配到Java堆中。
3.在这个阶段并不会像初始化阶段中那样会有初始化或者代码被执行。

解析阶段: 将类、接口、字段和方法的符号引用转换为直接引用,也就是得到类、接口、字段和方法在内存中的指针或偏移量。如果直接引用存在,那么可以肯定系统中存在该类、方法或者字段.但只存在符号引用,不能确定系统中一定存在该结构。解析操作往往会伴随着JVM在执行完初始化后在执行。


Initialization(初始化)阶段: 为类的静态变量赋予给定的初始值(显示初始化), 执行静态代码块

类的初始化是类装载的最后一个阶段。如果前面的步骤都没有问题,那么表示类可以顺利装载到系统中。此时,类才会开始执行Java字节码。(即:到了初始化阶段,才真正开始执行类中定义的Java程序代码。)

初始化阶段的重要工作是执行类的初始化方法:<clinit>()方法。 该方法仅能由Java编译器生成并由JVM调用,程序开发者无法自定义一个同名的方法 ,更无法直接在Java程序中调用该方法,虽然该方法也是由字节码指令所组成。它是由类静态成员的赋值语句以及static代码块合并产生的

<clinit>():只有在给类的static变量显示赋值或在静态代码块中赋值,才会生成此方法。初始化会调用静态代码块的方法

<init>():一定会出现在Class的method表中


子类加载前先加载父类吗?

是的。在加载一个类之前,虚拟机总是会试图加载该类的父类,因此父类的<clinit>()方法总是在子类<clinit>()之前被调用。也就是说,父类的static块优先级高于子类。

口诀:由父到子,静态先行

哪些类不会生成<clinit>方法?

Java编译器并不会为所有的类都产生<clinit>()初始化方法。哪些类在编译为字节码后,字节码文件中将不会包含<clinit>()方法?
1、一个类中并没有声明任何的类变量,也没有静态代码块时
2、一个类中声明类变量,但是没有明确使用类变量的初始化语句以及静态代码块来执行初始化操作时
3、一个类中包含static final修饰的基本数据类型的字段,这些类字段初始化语句采用编译时常量表达式或字面量
 

 static final的总结:f总结:

使用static final修饰的成员变量称为全局变量

什么时候在链接节点的准备环节:给全局常量赋值的是字面量或常量,不能涉及方法或构造器的调用。

除此之外,都是在初始化阶段赋值


类的初始化时机?类的初始化什么时候被调用?

类的初始化分为主动使用和被动使用

主动使用

Java虚拟机规定,一个类或接口在初次使用前,必须要进行初始化。这里指的“使用”是指主动使用。
主动使用只有下列几种情况:(即:如果出现如下的情况,则会对类进行初始化操作。而初始化操作之前的加载、验证、准备已经完成。)
1、当创建一个类的实例时,比如使用new关键字,或者通过反射、克隆、反序列化
2、当调用类的静态方法时,即当使用了字节码invokestatic指令。
3、当使用类、接口的静态字段时(final修饰特殊考虑),比如,使用getstatic或者putstatic指令。
4、当使用java.lang.reflect包中的方法反射类的方法时。比如:Class.forName(“com.atguigu.java.Test")
5、当初始化子类时,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
6、如果一个接口定义了default方法,那么直接实现或者间接实现该接口的类的初始化时,该接口要在其之前被初始化。
7、当虚拟机启动时,用户需要指定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类。

被动使用

除了以上的情况属于主动使用,其他的情况均属于被动使用。被动使用不会引起类的初始化
也就是说:
并不是在代码中出现的类,就一定会被加载或者初始化。如果不符合主动使用的条件,类就不会初始化
1、当访问一个静态字段时,只有真正声明这个字段的类才会被初始化。
   当通过子类引用父类的静态变量,不会导致子类初始化
2、通过数组定义类引用,不会触发此类的初始化
3、引用常量不会触发此类或接口的初始化。因为常量在链接阶段就已经被显式赋值了。
4、调用ClassLoader类的loadclass()方法加载一个类,并不是对类的主动使用,不会导致类的初始化。


Class的forName("Java.lang.String")和Class的getClassLoader()的loadClass("Java.lang.String")有什么区别?

Class.forName()是反射,会调用类的初始化<clinit>()方法,调用ClassLoader类的loadclass()方法不是对类的主动使用,不会导致类的初始化


一个类的字节码文件在JVM中会被加载几次?

不确定。但是只会被同一个类加载器加载一次。


加载什么?

在Java中数据类型分为基本数据类型和引用数据类型。基本数据类型由虚拟机预先定义,引用数据类型则需要进行类的加载

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值