Day353.类的加载过程(类的生命周期) -JVM
Posted 阿昌喜欢吃黄桃
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day353.类的加载过程(类的生命周期) -JVM相关的知识,希望对你有一定的参考价值。
类的加载过程(类的生命周期)
一、概述
- 注意:
我们所说的加载完毕包括:加载、链接、初始化三个阶段都完成之后类进入方法区中
- 大厂面试题
二、过程一:Loading(加载)阶段
1、加载完成的操作
2、二进制流的获取方式
3、类模型与Class实例的位置
堆空间中的大的Class对象,对应他的方法区内容;
4、数组类的加载
基本类型JVM虚拟机会已经加载;
对应引用类型就会去加载引用数据类型元素的类,String[],就是加载String类型
三、过程二:Linking(链接)阶段
1、环节1:链接阶段之Verification(验证)
对格式、语义、字节码、符号引用进行验证校验
验证阶段也不是绝对按顺序的
2、环节2:链接阶段之Preparation(准备)
对静态变量
分配内存,进行默认值赋值
static的变量赋初始值 如果是static final修饰的话就赋实际值
显式赋值: 直接赋实际值
默认初始化: 赋上默认值,值如上面表格图
对注意中的第1点分析:↓
注意:以下前3点的前提都是字段已经完成显示赋值(定义的后面已经赋了值)的前提下进行的
1、基本数据类型,
非final修饰的静态变量
会在准备阶段赋初始值
,然后在初始化
中的方法中显式赋值
2、
静态常量
(基本数据类型、String类型字面量("XXX"这种情况))在编译阶段会初始化赋值
,然后在准备阶段就会显式赋值
3、
引用数据类型的静态常量
,尤其是new String(“XXX”)这种形式,都是在初始化
中的中进行显式赋值
的4、如果在
static静态代码块中
具有显式赋值操作
(定义的后面没有赋值),那肯定就是在初始化
中的方法中显式赋值
3、环节3:链接阶段之Resolution(解析)
将符号引用转为直接引用
#num
就是符号引用
四、过程三:Initialization(初始化)阶段
静态变量的显式赋值
2.2中涉及的问题转化成代码如下所示:
< clinit >()方法什么时候编译器不会帮我们生产
1、static与final的搭配问题【难点】
最终结论:
final static,用到了new或者说用了其他类,就在clinit
使用static+final修饰,并且进行显式赋值(定义的时候后面就已经附了初始值),还不涉及到方法或者构造器调用的基本数据类型或者String类型字面量("XXX"这种形式,而不是new String(“XXX”)这种形式)的字段,将在准备中的
链接阶段的准备环节
进行显式赋值;其他已经进行显示赋值的静态常量(包括引用类型,尤其是new String(“XXX”)这种类型的,还有调用其他方法获得的值,比如new Random().nextInt(10)等)或者静态变量(这是肯定在初始化方法中显式赋值)都将在
初始化中<clinit>
的方法中进行显式赋值
对于准备阶段就完成赋值的,其字段下面的有属性ConstantValue,否则是没有属性ConstantValue的,如下:
2、< client >()的线程安全性
类的加载只会被执行一次
3、类的初始化情况:主动使用vs被动使用【难点】
主动使用就会去调clinit,被动使用就不会去调clinit;但是这个类肯定是被加载过了
注意:
1、反序列化会触发类的主动使用(调用()方法)
序列化和反序列和都会调用()方法,可以通过在Order类中写一个static静态代码块来测试
2、调用类、接口中显示赋值(定义之后就赋值,但是值只能是基本数据类型、字符串常量,不能是其他的,包括不能是new String(“XXX”)类型)的静态常量
不会触发类的主动使用(调用clinit()方法)
这是因为
静态常量
(有限制,在上面说明了)在编译时初始化,然后在链接中的准备阶段显示赋值,所以没有必要到初始化阶段,也就是不需要设计()方法就可以完成了,所以自然不会调用()方法;而显示赋值(定义后面就赋值)的
静态变量
,不是静态常量,他就需要在()方法中进行显示赋值,所以会触发类的初始化;而显示赋值(定义之后就赋值,但是值只能是基本数据类型、字符串常量,不能是其他的,包括不能是new String(“XXX”)类型)的静态常量需要在()方法中需要进行运算,所以需要进行显示赋值,所以会触发类的初始化,如 public static final int num2 = new Random().nextInt(2);
3、只要接口中存在default修饰的方法,无论是否调用该方法,即使是仅仅调用显示赋值(定义之后就赋值,但是值只能是基本数据类型、字符串常量,不能是其他的,包括不能是new String(“XXX”)类型)的静态常量就会触发类的主动使用(调用()方法)
我们知道可以通过static静态代码块来测试类的初始化,而测试接口的的初始化需要这样做:
4、执行类中main()方法的时候会先加载、链接、初始化该类,之后才会调用main()方法
5、子类使用父类的静态变量,会触发父类初始化,而不会触发子类初始化
由父及子,静态先行
5、数组是某类的类型,不会触发类的初始化
6、使用loadClass()方法加载一个类,不会触发类的初始化
4、-XX:+TraceClassLoading
-XX:+TraceClassLoading
具体使用如下:
五、过程四:类的Using(使用)
六、过程五:类的Unloading(卸载)
通常的情况下,类的卸载都很难进行
七、回顾:方法区的垃圾回收
以上是关于Day353.类的加载过程(类的生命周期) -JVM的主要内容,如果未能解决你的问题,请参考以下文章