加载

在加载阶段, 虚拟机需要完成以下3件事情:
1) 通过一个类的全限定名来获取定义此类的二进制字节流。
2) 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3) 在内存中生成一个代表这个类的java.lang.Class对象, 作为方法区这个类的各种数据的访问入口。
虚拟机规范的这3点要求其实并不算具体, 因此虚拟机实现与具体应用的灵活度都是相当大的。 例如“通过一个类的全限定名来获取定义此类的二进制字节流”这条, 它没有指明二进制字节流要从一个Class文件中获取, 准确地说是根本没有指明要从哪里获取、 怎样获取。虚拟机设计团队在加载阶段搭建了一个相当开放的、 广阔的“舞台”, Java发展历程中, 充满创造力的开发人员则在这个“舞台”上玩出了各种花样, 许多举足轻重的Java技术都建立在这一基础之上, 例如:从ZIP包中读取, 这很常见, 最终成为日后JAR、 EAR、 WAR格式的基础。从网络中获取, 这种场景最典型的应用就是Applet。

验证

  Java语言本身是相对安全的语言( 依然是相对于C/C++来说),使用纯粹的Java代码无法做到诸如访问数组边界以外的数据、 将一个对象转型为它并未实现的类型、 跳转到不存在的代码行之类的事情, 如果这样做了, 编译器将拒绝编译。 但前面已经说过, Class文件并不一定要求用Java源码编译而来, 可以使用任何途径产生, 甚至包括用十六进制编辑器直接编写来产生Class文件。 在字节码语言层面上, 上述Java代码无法做到的事情都是可以实现的,至少语义上是可以表达出来的。 虚拟机如果不检查输入的字节流, 对其完全信任的话, 很可能会因为载入了有害的字节流而导致系统崩溃, 所以验证是虚拟机对自身保护的一项重要工作。

验证阶段大致上会完成下面4个阶段的检验动作:

文件格式验证:

  验证字节流是否符合Class文件格式的规范

元数据验证:

  对类的元数据信息进行语义校验

字节码验证:

  主要目的是通过数据流和控制流分析,确定程序语义是合法的、 符合逻辑的。

符号引用验证:

  符号引用验证可以看做是对类自身以外( 常量池中的各种符号引用) 的信息进行匹配性校验, 通常需要校验下列内容:符号引用中通过字符串描述的全限定名是否能找到对应的类。在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段。符号引用中的类、 字段、 方法的访问性( private、 protected、 public、 default) 是否可被当前类访问。

准备

准备阶段是正式为类变量分配内存并设置类变量初始值的阶段, 这些变量所使用的内存都将在方法区中进行分配。

public static int value=123;   验证阶段value=零值
#因为这时候尚未开始执行任何Java方法, 而把value赋值为123的putstatic指令是程序被编译后, 存放于类构造器< clinit> ( ) 方法之中, 所以把value赋值为123的动作将在初始化阶段才会执行。

public static final int value=123; 验证阶段value=123
#如果类字段的字段属性表中存在ConstantValue属性, 那在准备阶段变量value就会被初始化为ConstantValue属性所指定的值。编译时Javac将会为value生成ConstantValue属性, 在准备阶段虚拟机就会根据ConstantValue的设置将value赋值为123

解析

解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程。

  • 符号引用( Symbolic References):

符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量, 只要使用时能无歧义地定位到目标即可。 符号引用与虚拟机实现的内存布局无关, 引用的目标并不一定已经加载到内存中。 各种虚拟机实现的内存布局可以各不相同, 但是它们能接受的符号引用必须都是一致的, 因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。(编译的时候,不知道存储地址,用符号)

  • 直接引用( Direct References):

直接引用可以是直接指向目标的指针、 相对偏移量或是一个能间接定位到目标的句柄。 直接引用是和虚拟机实现的内存布局相关的, 同一个符号引用在不同虚拟机实例上翻译出来的直接引用一般不会相同。 如果有了直接引用, 那引用的目标必定已经在内存中存在。(加载后,知道在内存中的地址,吧之前在class中的符号替换为指针、或句柄)

  解析动作主要针对类或接口、 字段、 类方法、 接口方法、 方法类型、 方法句柄和调用点限定符7类符号引用进行 
关于理解“将常量池内的符号引用替换为直接引用”  参见:  https://blog.csdn.net/qq_34402394/article/details/72793119

初始化

前面的动作完全由虚拟机主导和控制。 到了初始化阶段, 才真正开始执行类中定义的Java程序代码( 或者说是字节码) 。

public class Test{
static{
i=0; //给变量赋值可以正常编译通过
System.out.print( i) ; //这句编译器会提示"非法向前引用"
}
static int i=1;
}

<clinit>()方法对于类或接口来说并不是必需的,如果一个类中没有静态语句块,也没有对变量的赋值操作,那么编译器可以不为这个类生成<clinit>()方法。

<clinit>() 方法类和接口区别:

虚拟机会保证在子类的<clinit>() 方法执行之前,父类的<clinit>()方法已经执行完毕。也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作

  • 接口

接口与类不同的是,执行接口的<clinit>()方法不需要先执行父接口的<clinit>()方法。只有当父接口中定义的变量使用时,父接口才会初始化。

  虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞,在实际应用中这种阻塞往往是很隐蔽的。同一个类加载器下,一个类型只会初始化一次。

ps:可以想一下,为什么mysql使用jdbc的驱动是需要 Class.forName("com.mysql.jdbc.Driver");

为什么不直接new  com.mysql.jdbc.Driver(),对象而是要去DriverManager注册。

参考《深入理解java虚拟机》

java jvm虚拟机类加载过程的更多相关文章

  1. JVM虚拟机 类加载过程与类加载器

    目录 前言 类的生命周期 类加载过程 加载 连接 验证 准备 解析 初始化 类加载器 三大类加载器 双亲委派模型 概念 为什么要使用双亲委派模型 源码分析 反双亲委派模型 参考 前言 类装载器子系统是 ...

  2. java jvm虚拟机类加载器

    在Java中任意一个类都是由这个类本身和加载这个类的类加载器来确定这个类在JVM中的唯一性. 类加载器 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到J ...

  3. JVM的类加载过程以及双亲委派模型详解

    JVM的类加载过程以及双亲委派模型详解 这篇文章主要介绍了JVM的类加载过程以及双亲委派模型详解,类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象 ...

  4. jvm学习002 虚拟机类加载过程以及主动引用和被动引用

    虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类从被加载到虚拟机内存中开始,到卸载出内存为 ...

  5. JVM虚拟机-类加载器子系统

    转自博客:http://www.cnblogs.com/muffe/p/3541189.html   还有一些自己补充的知识点 一.类加载器基本概念 顾名思义,类加载器(class loader)用来 ...

  6. 深入理解JVM - 虚拟机类加载机制 - 第七章

    类加载的时机类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括了:加载/验证/准备/解析/初始化/使用/卸载七个阶段.其中验证/准备和解析统称为连接(Linking). 加载.验证.准 ...

  7. JVM 虚拟机&&类加载(一)

    虚拟机 虚拟机简介 Java 虚拟机(JVM)是运行java程序的抽象计算机,它是计算机设备的规范,可以采用不同方式进行实现,java 程序通过运行在JVM中实现跨平台,一次编译到处运行,不同的操作系 ...

  8. 类文件的结构、JVM 的类加载过程、类加载机制、类加载器、双亲委派模型

    一.类文件的结构 我们都知道,各种不同平台的虚拟机,都支持 "字节码 Byte Code" 这种程序存储格式,这构成了 Java 平台无关性的基石.甚至现在平台无关性也开始演变出 ...

  9. Java JVM——2.类加载器子系统

    概述 类加载器子系统在Java JVM中的位置 类加载器子系统的具体实现 类加载器子系统的作用 ① 负责从文件系统或者网络中加载.class文件,Class 文件在文件开头有特定的文件标识. ② Cl ...

随机推荐

  1. AcWing 154. 滑动窗口 单调队列

    地址 https://www.acwing.com/problem/content/description/156/ 输入格式 输入包含两行. 第一行包含两个整数n和k,分别代表数组长度和滑动窗口的长 ...

  2. WPF Datagrid 动态生成列 并绑定数据

    原文:WPF Datagrid 动态生成列 并绑定数据 说的是这里 因为列头是动态加载的 (后台for循环 一会能看到代码) 数据来源于左侧列 左侧列数据源 当然num1 属于临时的dome使用  可 ...

  3. 第04组 Beta冲刺(4/5)

    队名:new game 组长博客 作业博客 组员情况 鲍子涵(队长) 过去两天完成了哪些任务 地图移动 接下来的计划 素材和脚本相连 引入声音素材 还剩下哪些任务 让游戏本体运行 遇到了哪些困难 时间 ...

  4. gRPC+gRPC Gateway+swagger小记

    前言 本文记录了grpc-gateway的简单使用. 定义proto 先来看看最常规的 syntax = "proto3"; package protos; service Gre ...

  5. PAT 1002 A+B for Polynomials(map模拟)

    This time, you are supposed to find A+B where A and B are two polynomials(多项式). Input Each input fil ...

  6. springboot with appache sharding 3.1 单库分表

    配置文件相关信息: #开发 server.port=7200 spring.application.name=BtspIsmpServiceOrderDev eureka.client.service ...

  7. sql server 随记 -- 月份/日期 查询

    月份 SELECT * FROM ( ] ) a UNPIVOT ( xxx ]) ) a2; 日期 SELECT * FROM ( ] )a UNPIVOT( xxx ]) )a2 (存档)

  8. 骚操作!曾经爱过!用 Python 清理收藏夹里已失效的网站

    前言 本文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: 小詹&有乔木 PS:如有需要Python学习资料的小伙伴可 ...

  9. JS基础语法---for循环遍历数组

    for循环遍历数组 要显示数组中的每个数据,可以如下: var arr=[10,20,30,40,50]; //显示数组中的每个数据 console.log(arr[0]); console.log( ...

  10. CSS的border-radius 设置圆弧

    现象:将div变为有一定幅度的圆形.椭圆形等 方法:使用css的border-radius 属性进行设置CSS3 border-radius 属性:向 div 元素添加圆角边框: 一:首先建立一个di ...