java jvm虚拟机类加载过程
加载
在加载阶段, 虚拟机需要完成以下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虚拟机类加载过程的更多相关文章
- JVM虚拟机 类加载过程与类加载器
目录 前言 类的生命周期 类加载过程 加载 连接 验证 准备 解析 初始化 类加载器 三大类加载器 双亲委派模型 概念 为什么要使用双亲委派模型 源码分析 反双亲委派模型 参考 前言 类装载器子系统是 ...
- java jvm虚拟机类加载器
在Java中任意一个类都是由这个类本身和加载这个类的类加载器来确定这个类在JVM中的唯一性. 类加载器 虚拟机设计团队把类加载阶段中的“通过一个类的全限定名来获取描述此类的二进制字节流”这个动作放到J ...
- JVM的类加载过程以及双亲委派模型详解
JVM的类加载过程以及双亲委派模型详解 这篇文章主要介绍了JVM的类加载过程以及双亲委派模型详解,类加载器就是根据指定全限定名称将 class 文件加载到 JVM 内存,然后再转化为 class 对象 ...
- jvm学习002 虚拟机类加载过程以及主动引用和被动引用
虚拟机把描述类的数据从class文件加载到内存,并对数据进行校验,转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制. 类从被加载到虚拟机内存中开始,到卸载出内存为 ...
- JVM虚拟机-类加载器子系统
转自博客:http://www.cnblogs.com/muffe/p/3541189.html 还有一些自己补充的知识点 一.类加载器基本概念 顾名思义,类加载器(class loader)用来 ...
- 深入理解JVM - 虚拟机类加载机制 - 第七章
类加载的时机类从被加载到虚拟机内存开始,到卸载出内存为止,它的整个生命周期包括了:加载/验证/准备/解析/初始化/使用/卸载七个阶段.其中验证/准备和解析统称为连接(Linking). 加载.验证.准 ...
- JVM 虚拟机&&类加载(一)
虚拟机 虚拟机简介 Java 虚拟机(JVM)是运行java程序的抽象计算机,它是计算机设备的规范,可以采用不同方式进行实现,java 程序通过运行在JVM中实现跨平台,一次编译到处运行,不同的操作系 ...
- 类文件的结构、JVM 的类加载过程、类加载机制、类加载器、双亲委派模型
一.类文件的结构 我们都知道,各种不同平台的虚拟机,都支持 "字节码 Byte Code" 这种程序存储格式,这构成了 Java 平台无关性的基石.甚至现在平台无关性也开始演变出 ...
- Java JVM——2.类加载器子系统
概述 类加载器子系统在Java JVM中的位置 类加载器子系统的具体实现 类加载器子系统的作用 ① 负责从文件系统或者网络中加载.class文件,Class 文件在文件开头有特定的文件标识. ② Cl ...
随机推荐
- IDEA2019 Win10 Tomcat Server控制台中文乱码的快速解决办法
原理 Windows10的控制台使用GBK编码,而Tomcat使用UTF-8编码,导致乱码 解决办法 修改$tomcat/conf/logging.properties文件 # 注释这行 java.u ...
- 通过yum在centos安装mysql并配置远程登录
前言 前天按照Oracle上的文档装了一遍mysql,选了最新8.0的版本,后来出现一些问题,网上搜答案,出来的基本还是5.x版本的解决方案,并不适用8.0版本.然后我就去看了一下公司的正式环境买的阿 ...
- golang--redis基本介绍
redis(remote-dictionary-system)即远程字典服务器,是NoSQL数据库: 适合做缓存以及持久化: 免费开源,高性能的分布式内存数据库: redis的安装和使用: 下载Red ...
- Java反射简单使用--第一次细致阅读底层代码
1:所写的东西都经过验证,保证正确,环境jdk8,eclipse2:在例子中,尽量以生产环境中实际代码为例,那种固定值什么的没什么意义 问题: 1:想获取调用方法所需要的参数 2:参数是以json形式 ...
- allure定制化输出测试报告,让报告锦上添花!
一.定制化后的效果展示 用两张图展示效果: 二.注意别踩坑 allure定制化想必大部分情况都会去选择pip install pytest-allure-adaptor这个插件,安装完成后,运行定制化 ...
- 【CodeChef】Find a special connected block - CONNECT(斯坦纳树)
[CodeChef]Find a special connected block - CONNECT(斯坦纳树) 题面 Vjudge 题解 还是一样的套路题,把每个数字映射到\([0,K)\)的整数, ...
- angular 前端路由不生效解决方案
angular 前端路由不生效解决方案 Intro 最近使用 Angular 为我的活动室预约项目开发一个前后端分离的客户端,在部署上遇到了一个问题,前端路由不生效,这里记录一下.本地开发正常,但是部 ...
- oracle学习笔记(十六) PL/SQL 异常和goto语句
PL/SQL 异常和goto语句 异常 预定义异常 oracle常见预定义异常: 错误号 异常错误信息名称 说明 ORA-0001 DUP_VAL_ON_INDEX 试图破坏一个唯一性限制 ORA-0 ...
- vue-基本动画
不使用动画 <div id="app"> <input type="button" value="toggle" @cli ...
- python基础(13):函数名的使用、第一类对象、闭包、迭代器
1. 函数名的运用 函数名是⼀个变量,但它是⼀个特殊的变量,与括号配合可以执⾏函数的变量. 1.1 函数名的内存地址 def func(): print("呵呵") print(f ...