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的主要内容,如果未能解决你的问题,请参考以下文章

类的生命周期

JVM类生命周期概述:加载时机与加载过程

JVM 类的加载过程

类的生命周期

java类的生命周期

java类的生命周期