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 ...
随机推荐
- spider-通过scrapyd网页管理工具执行scrapy框架
1.首先写一个scrapy框架爬虫的项目 scrapy startproject 项目名称 # 创建项目 cd 项目名称 scrapy genspider 爬虫名称 爬虫网址(www.xxxx) #生 ...
- "One or more types required to compile a dynamic expression cannot be found. Are you missing references to Microsoft.CSharp.dll and System.Core.dll?"的解决方法
#事故现场: 在一个.net 4.0 的项目中使用dynamic,示例代码如下: private static void Main(string[] args) { dynamic obj; obj ...
- TypeScript + Webpack 4 开发环境搭建(转)
前段时间接触到 Microsoft 的 Microsoft.AspNetCore.SpaTemplates 模板,生成的项目使用的默认语言是 TypeScript,虽然以前在此之前并没有用过TypeS ...
- html公用头部和尾部
这个方式比较简单,样式和js也有效果,还有object和iframe方式 效果图,可以看出公共的样式对于引入的文件也有效果,在加载完文件后js也是有效果的 index.html header.html ...
- 【OCR系列之一】字符识别技术总览
最近入坑研究OCR,看了比较多关于OCR的资料,对OCR的前世今生也有了一个比较清晰的了解.所以想写一篇关于OCR技术的综述,对OCR相关的知识点都好好总结一遍,以加深个人理解. 什么是OCR? OC ...
- 转载-ThreadPoolExecutor里面4种拒绝策略(详细)
原文链接:https://blog.csdn.net/wjs19930820/article/details/79849050 1 /** * 定义异步任务执行线程池 */ @Configuratio ...
- java线程join方法使用方法简介
本博客简介介绍一下java线程的join方法,join方法是实现线程同步,可以将原本并行执行的多线程方法变成串行执行的 如图所示代码,是并行执行的 public class ThreadTest { ...
- PostgreSQL 12 YUM安装
目录 1.创建postgres用户 2.查看操作系统版本 3.配置yum源(对应CentOS 6) 4.安装客户端包 5.安装服务器端包 6.初始化数据库和设置自启动服务 7.postgres用户的b ...
- 用redis-dump工具对redis集群所有数据进行导出导入
安装redis-dump redis-dump是基于ruby开发,需要ruby环境,而且新版本的redis-dump要求2.2.2以上的ruby版本,centos中yum只能安装2.0版本的ruby. ...
- vue拖拽组件开发
vue拖拽组件开发 创建临时vue项目 先查看node和npm版本,怎么安装就不多多bb了 再安装vue-cli npm install vue-cli -g //全局安装 vue-cli 检测是否安 ...