【JVM第一篇--类加载机制】类加载过程
写在前面的话:本文是在观看尚硅谷JVM教程后,整理的学习笔记。其观看地址如下:尚硅谷2020最新版宋红康JVM教程
一、什么是类加载过程
(1)、概述
我们编写的类(.java文件)会被编译器(如javac编译器)编译成Class文件。Java虚拟机把Class文件加载到内存中的过程就称为类加载过程。
(2)、类的生命周期
- 一个类从被加载到虚拟机内存中,到卸载出内存,共经历七个过程,即这个类的生命周期会经历加载、验证、准备、解析、初始化、使用、卸载七个阶段。其中,验证、准备、解析三个阶段又统称为连接。图示如下:

下面我们将逐个介绍类生命周期每个阶段的执行过程。
二、加载阶段
加载阶段是整个类加载过程的第一个阶段。
在本阶段,Java虚拟机主要完成以下三件事:
(1)、 通过一个类的全限定名称获取定义此类的二进制字节流。
(2)、 将该字节流所代表的静态存储结构转化为方法区中数据结构。
(3)、 在内存中生成一个代表该类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。
Java虚拟机运行时数据区示意图如下:

总结概括就是,先将Class文件以字节流的形式加载到内存,再把这个字节流放到虚拟机运行时数据区的方法区中,以及在内存中生成一个代表该类的Class对象,作为方法区中该类的访问入口。
三、验证阶段
(1)、 验证阶段目的是确保Class文件中的字节流中包含的信息符合虚拟机规范为约束要求,保证被加载类的正确性不会威胁到虚拟机自身的安全。
(2)、 验证的内容有,
文件格式验证:字节流是否符合Class文件格式。
元数据验证:对字节码描述的信息进行语义分析。
字节码验证:确定程序语义是否合法、符合逻辑。
符号引用验证:该类是否缺少或禁止访问它依赖的外部资源,如其他类、方法等。
四、准备阶段
准备阶段是正式为类中定义的静态变量(static修饰的变量)分配内存并设置零值。
零值:虚拟机为基本数据类型设置的初始值,比如int类型的零值为0,boolean类型的零值为false。
比如, private static int a = 123,在准备阶段过后,a的初始值为0,而不是123。而 a = 123这个赋值动作是在初始化阶段进行的。
通常情况下,准备阶段的静态变量的初始值是零值。但是,如果静态变量被 final 修饰,则可能不会是零值。如 private final static int b = 123 ,则 b 的初始值是123,而不是零值。因为 final static修饰的 b 为常量,在编译时就已经被赋值了,即在被编译成字节码的时候就已经是 b = 123;
五、解析阶段
解析阶段是将常量池中的符号引用替换为直接引用的过程。
符号引用:以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。
直接引用:是可以直接指向目标的指针、相对偏移量、或者句柄。
解析阶段主要涉及到符号引用和直接引用的转换,但不影响了解整个类加载过程,故先略过,以后再补充。
六、初始化阶段
初始化时类加载过程的最后一个阶段,在前面的几个类加载动作中,除了加载阶段用户编写的代码(应用程序)可以通过自定义的类加载器参与类加载外,其余阶段的类加载动作都是虚拟机完成的。直到初始化阶段,虚拟机才真正执行类中用户编写的Java代码。
类加载器:前面写到,在加载阶段虚拟机需要完成“通过一个类的全限定名称获取定义此类的二进制字节流”这个动作,虚拟机将这个动作交给应用程序,让其自行去决定怎么获取所需的类。而实现这个动作的代码就被称为类加载器。
初始化阶段就是执行类构造器 < clinit >()方法的过程。
< clinit >()方法不是我们直接编写的Java代码,而是javac编译器的自动生成物。
关于< clinit >()方法,需要知道一下几点:
(1)、 此方法由javac编译器自动收集类中所有类变量的赋值动作和静态代码块中的语句合并而成(即static修饰的变量和static{}),按语句的顺序执行。
如下代码所示:
public class HelloClinit {
//num = 1 和 static{} 的过程就是 <clinit>()的过程
private static int num = 1;
static{
num = 2;
a = 2; //可以为之后定义的变量赋值,但不能访问
//System.out.println(a); //报错
}
private static int a;
public static void main(String[] args) {
System.out.println(num+","+a);
}
}
输出结果为:2,2
(2)、 < clinit >()方法不需要我们自己去调用父类的< clinit >()方法。因为在子类的< clinit >()执行前,虚拟机会保证父类的< clinit >()已执行完毕
代码如下:
/**
* Java虚拟机会保证在子类的<clinit>()执行前,父类的<clinit>()先执行,
* 这也意味着父类中定义的静态代码块要优于子类中的变量赋值操作
*/
public class HelloClinit {
static class father{
//父类的静态变量负责语句a = 1和静态代码块static{}就是父类的<clinit>()
public static int a = 1;
static {
a = 2;
}
}
static class son extends father{
public static int b = 0;
static {
b = a; //故子类在调用的父类的变量a时,实际上a = 2
}
}
public static void main(String[] args) {
System.out.println(son.b); //2
}
}
输出结果为:2
(2)、 Java虚拟机会保证一个类的clinit()方法在多线程环境中被正确的同步加锁,即如果多个线程同时去初始化一个类,那么只有其中一个线程去执行这个类的()方法,其他线程都要阻塞的等待,直到活动线程执行完()方法,即一个类的clinit()只会被加载一次。
下面的代码模拟一条线程在死循环状态下操作,而另一条线程无限阻塞等待的过程。
public class ClinitThread {
public static void main(String[] args) {
/**
* 下面的代码创建两个线程,让他们都去创建DeadThread类的对象,
* 但实际上只有线程1会执行<clinit>()方法,即执行static{}语句块,并陷入死循环
* 而线程2则会阻塞等待
*/
Runnable r = () ->{
System.out.println(Thread.currentThread().getName()+"开始");
DeadThread deadThread = new DeadThread();
System.out.println(Thread.currentThread().getName()+"结束");
};
Thread t1 = new Thread(r,"线程1");
Thread t2 = new Thread(r,"线程2");
t1.start();
t2.start();
}
}
class DeadThread{
static {
if (true){
System.out.println(Thread.currentThread().getName()+"初始化当前类");
while(true); //陷入死循环
}
}
}
以上几个阶段就是类加载的大致过程
七、拓展补充
类加载的时机
- (1)、在生命周期图中,加载、验证、准备、初始化、卸载。这五个阶段的顺序是确定的,即只有前一个阶段开始,后一个阶段才能开始。但是解析阶段是不确定的,因为它在一些情况下可以在初始化过程之后再开始。注意这个说的是阶段开始,而并不是阶段完成,因为这些阶段通常都是交叉混合进行的。比如在加载阶段已开始但尚未完成时,验证阶段可能已经开始了。
- (2)、关于什么时候开始类加载的第一阶段加载,具体由虚拟机自身来把握实现,没有明确的规定。但是,对于类加载中的初始化阶段,有且只有以下六种情况必须立即对类进行初始化操作。这也意味着,在初始化操作前,加载,验证、准备也已经开始了。
具体的六种情况如下:
① 遇到new、getstatic、putstatic或invokestatic这四条字节码指令的时候,如果类没有进行初始化,则需要先触发其初始化.
生成这四条指令的最常见的java代码场景是:
1.使用new关键字实例化对象的时候
2.读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候
3.调用一个类的静态方法的时候
② 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有进行初始化,则需要先触发其初始化
③ 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
④ 当虚拟机启动的时候,用户需要制定一个要执行的主类(包含main方法的那个类),虚拟机会先初始化这个主类
⑤ 当使用jdk7新加入的动态语言支持的时候,如果一个java,lang.invoke.MethodHandler实例的最后解析结果是REF_getStatic,REF_putStatic,REF_invokeStatic,REF_newInvokeSpecial四种类的方法句柄,并且这个方法句柄对应的类没有进行过初始化,那么需要先触发其初始化.
⑥ (新)当一个接口中定义了JDK8新加入的默认方法(被default关键字修饰的接口方法)时,如果这个接口的实现类发生了初始化,那么该接口要在其之前初始化
以上几种使用类的情况称为类的主动使用,除了以上6种外,其他使用类的方式都称为类的被动使用,即不会导致类的初始化。
【JVM第一篇--类加载机制】类加载过程的更多相关文章
- 深度分析:Java虚拟机类加载机制、过程与类加载器
虚拟机类加载机制是把描述类的数据从 Class 文件加载到内存,并对数据进行校验.转换解析和初始化,最终形成可以被虚拟机直接使用的 Java 类型. 需要注意的是 Java 语言与其他编译时需要进 ...
- JVM类加载机制---类加载的过程
一.类加载的时机 类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载.验证.准备.解析.初始化.使用.卸载 7个阶段,其中验证.准备.解析 3个部分统称为 连接. 二.具体步骤 ...
- 面试之三:JVM类加载机制-类加载各阶段说明和类加载器
一.类生命周期:共7个阶段 类从被加载到虚拟机内存中开始,到卸载出内存.整个生命周期包括:加载.验证.准备.解析.初始化.使用和卸载7个阶段. 其中验证.准备.解析3个部分统称为连接. 类加载的过程: ...
- JVM类加载机制---类加载器
一.概念 "通过一个类的全限定名来获取描述此类的二进制字节流",实现这个动作的代码模块成为 类加载器. 二.分类 从java开发人员的角度出发,系统提供的类加载器大致分为如下3类: ...
- 初步了解JVM第一篇
大家都知道,Java中JVM的重要性,学习了JVM你对Java的运行机制.编译过程和如何对Java程序进行调优相信都会有一个很好的认知. 废话不多说,直接带大家来初步认识一下JVM. 什么是JVM? ...
- JVM 第一篇:编译 OpenJdk14 ,我行你也行
本文内容过于硬核,建议有 Java 相关经验人士阅读. 1 引言 从上周开始一直在看周志明的 「深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)」 ,好多年之前看过第二版的,绝对算的上是国内 ...
- JVM学习——类加载机制(学习过程)
JVM--类加载机制 2020年02月07日14:49:19-开始学习JVM(Class Loader) 类加载机制 类加载器深入解析与阶段分解 在Java代码中,类型的加载.连接与初始化过程中都是在 ...
- JVM,Tomcat与OSGi类加载机制比较
首先一个思维导图来看下Tomcat的类加载机制和JVM类加载机制的过程 类加载 在JVM中并不是一次性把所有的文件都加载到,而是一步一步的,按照需要来加载. 比如JVM启动时,会通过不同的类加载器加载 ...
- Java 类加载机制 ClassLoader Class.forName 内存管理 垃圾回收GC
[转载] :http://my.oschina.net/rouchongzi/blog/171046 Java之类加载机制 类加载是Java程序运行的第一步,研究类的加载有助于了解JVM执行过程,并指 ...
随机推荐
- mysql任意文件读取漏洞复现
前言 第一次得知该漏洞后找了一些文章去看. 一开始不明白这个漏洞是怎么来的,只知道通过在服务端运行poc脚本就可以读取客户端的任意文件,直接找到网上准备好的靶机进行测试,发现可行,然后就拿别人的poc ...
- volatile、ThreadLocal的使用场景和原理
并发编程中的三个概念 原子性 一个或多个操作.要么全部执行完成并且执行过程不会被打断,要么不执行.最常见的例子:i++/i--操作.不是原子性操作,如果不做好同步性就容易造成线程安全问题. 可见性 多 ...
- .net 手动建DataTable 获取DataTable列名 修改DataTable 列的顺序
//创建 表 DataTable tables = new DataTable(); //添加 创建 列 //第一列 DataColumn cums = new DataColumn(); cums. ...
- 基于python实现单链表代码
1 """ 2 linklist.py 3 单链表的构建与功能操作 4 重点代码 5 """ 6 7 class Node: 8 " ...
- C语言编程丨循环链表实现约瑟夫环!真可谓无所不能的C!
循环链表 把链表的两头连接,使其成为了一个环状链表,通常称为循环链表. 和它名字的表意一样,只需要将表中最后一个结点的指针指向头结点,链表就能成环儿,下图所示. 需要注意的是,虽然循环链表成环 ...
- Flink on Yarn三部曲之三:提交Flink任务
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- VMware Workstatition启动虚拟机电脑蓝屏
电脑出了点问题,重装了系统,结果安装VMware之后,一启动虚拟机电脑就蓝屏重启. 系统是win10 19041 开始用的原来下载的vmware15.0,创建虚拟机蓝屏,重启之后可以创建.创建完以后启 ...
- Redis 五种数据结构详解(string,hash,list,set,zset)
一.五种数据结构: 1. String--字符串 String 数据结构是简单的 key-value 类型,value 不仅可以是 String,也可以是数字(当数字类型用 Long 可以表示的时候e ...
- 【0】TensorFlow光速入门-序
本文地址:https://www.cnblogs.com/tujia/p/13863181.html 序言: 对于我这么一个技术渣渣来说,想学习TensorFlow机器学习,实在是太难了: 百度&qu ...
- MongoDB简介---MongoDB基础用法(一)
Mongo MongoDB是一个基于分布式文件存储的数据库.MongoDB是一个介于关系数据库和非关系数据库之间的产品,是非关系数据库当中功能最丰富,最像关系数据库的. MongoDB 将数据存储为一 ...