JVM笔记4-虚拟机类加载机制

1、概述

Java虚拟机把描述类的数据从Class文件加载到内存中,并对数据进行检验、转换解析和初始化,最终形成可以被虚拟机直接使用的Java类型。这个过程称为虚拟机的类加载机制。

2、类加载的时机

一个类型从被加载到内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化(Initialization)、使用(Using)和卸载(Unloading)七个阶段。其中验证、准备、解析统称为连接阶段。
image.png
其中,加载、验证、准备、初始化、卸载这五个阶段的顺序是确定的。类型的加载过程必须按照这个顺序按部就班的开始。但是解析阶段则不一定:它在某些情况下可以在初始化阶段才开始,这是为了支持Java语言的运行时动态绑定特性。

3、类加载过程

3.1、加载

“加载”是整个“类加载”(Class Loading)过程中的一个阶段。这两个不是同一个东西。
在加载阶段,Java虚拟机主要完成以下三件事情:
1、通过一个类的全限定名来获取定义此类的二进制字节流。
2、将这个字节流所代表的静态存储结构转换为方法区上的运行时数据结构。
3、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口。

3.2、验证

“验证”阶段是连接阶段的第一个阶段,这个阶段是为了保证Class文件的字节流包含的信息符合《Java虚拟机规范》的全部约束要求,保证这些信息被当做代码运行后不会危害虚拟机自身的安全。

3.2.1、文件格式验证

验证阶段的第一个阶段,主要验证字节流是否符合Class文件格式规范,并且能被当前的虚拟机版本处理。这一阶段可能包括下面这些验证点:
1、是否以魔数0xCAFEBABE开头。
2、主、次版本号是否在当前Java虚拟机接受范围内。
3、常量池中的常量是否有不被支持的常量类型(检查常量tag标志)。

这个阶段的验证是基于二进制流进行的,只有通过了这个阶段的验证之后,这段字节流才会被允许进入Java虚拟机内存的方法区中进行存储。所以后面的三个阶段都是基于方法区的存储结构进行的,不会在读取、操作字节流了。

3.2.2、元数据验证

验证阶段的第二个阶段,这一阶段对字节码描述的信息进行语义分析,以保证其描述的信息符合《Java语言规范》要求。这个阶段可能包括的验证点如下:
1、这个类是否有父类(除了java.lang.Object之外,所有的类都应当有父类)。
2、这个类的父类是否继承了不允许被继承的类(final修饰的类)。
3、如果这个类不是抽象类,是否实现了父类或接口之中要求实现的所有方法。

3.2.3、字节码验证

验证阶段的第三个阶段,这一阶段主要是通过数据流分析和控制流分析,确定程序语义是否合法、符合逻辑的。在“元数据验证”阶段完成对数据类型校验完毕之后,这阶段主要对类的方法体(Class文件中的code属性)进行校验分析,保证被校验的方法在运行时不会做出对虚拟机安全的行为。例如:
1、保证任何跳转行为,都不会跳转到方法体以外的字节码指令上。
2、保证方法体中的类型转换是有效的。比如:将子类赋值给父类是有效,但是将父类赋值给子类或者赋值给完全不相干的一个类,则是危险和不合法的。

3.2.4、符号引用验证

验证阶段的最后一个阶段,这个阶段的检验行为发生在虚拟机将符号引用转换为直接引用的时候,这个转换动作发生在连接的第三个阶段——解析阶段中发生。符号引用验证可以看做是对类自身以外的各种信息进行匹配性校验。通俗来说就是该类是否缺少或者被禁止访问它依赖的某些外部类、方法、字段等资源。
1、在指定类中是否存在符合方法的字段描述符以及简单名称所描述的方法和字段。
2、符合引用中的类、字段、方法的可访问性(private、protected、public、)是否可被当前类访问。

3.3、准备

准备阶段是正是为类中定义的变量(即静态变量,被static修饰的变量)分配内存并设置类变量初始值的阶段。从概念上来讲,这些变量所使用的内存都应当在方法区中进行分配。但是必须注意到方法区本身是一个逻辑上的区域。在JDK7及之前,HotSpot使用永久代来实现方法区时,实现是完全符合这个逻辑概念的。但是在JDK8及之后,类变量则随着Class对象一起存放在Java堆中,这时候类变量在方法区就是一种逻辑概念的描述。
在准备阶段,这时候进行的内存分配只有类变量,而不包括实例变量,实例变量将在对象实例化时随着对象一起分配在堆上。且这里所说的初始值“通常情况”下是数据类型的零值。例如一个类的变量定义为:

public static int value=123;

那变量value在准备阶段过后的初始值为0而不是123。由于这时没有执行过任何的Java方法,而把value赋值123的putstatic指令是在程序编译后,存放在类构造器()方法中的,所以把value赋值123的动作要到初始化阶段才会被执行。

Java中所有基本类型零值:
image.png
上面提到的“通常情况”下的初始值是零值,但是也存在特殊情况:如果类变量字段属性表中存在ConstantValue属性,那么在准备阶段变量值就会被初始化为ConstantValue属性所指定的初始值。假设上面的变量value定义变为:

public static final int value=123;
**编译时Javac将会为value生成ConstantValue属性,在准备阶段虚拟机会根据ConstantValue的设置将value赋值为123。**

3.4、解析

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

3.5、初始化

类的初始化阶段,是类加载过程的最后一个阶段。在之前的几个类加载阶段里,除了加载阶段用户应用程序可以通过自定义类加载器的方式局部参与外,其余动作都完全由Java虚拟机来主导控制。知道初始化阶段,java虚拟机才真正开始执行类中编写的Java程序代码,将主导权交给应用程序。
在准备阶段,变量已经赋过一次系统要求的零值,而在初始化阶段,才会根据程序员的编码来赋值类变量和其他资源。简答的来说,在初始化阶段才会执行()方法。()并不是程序员在Java代码中直接编写的方法,它时javac编译器自动生成物。
1、()方法是由编译器自动收集类中的所有类变量的赋值动作和静态变量语句块(static{})中的语句合并产生的,编译器收集的顺序是由语句在源文件中的顺序决定的,静态语句块中只能访问到定义在静态语句块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问。例如:

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

image.png
2、()放与类的构造函数不同,它不需要显式的调用父类构造器,Java虚拟机会保证子类的()方法执行前,父类的()方法已经执行完毕。因此在Java虚拟机中第一个被执行的()方法的类型一定是java.lang.Object。
3、由于父类的()方法先执行,也就意味着父类中定义的静态语句块要优先于子类的变量赋值操作。例如:下面的代码,字段B的值是2而不是1。

static class parent{public static int A=1;static{A=2;}
}public static class sub extends parent{public static int B=A;
}

4、Java虚拟机必须保证一个类的()方法在多线程环境下被正确的加锁同步,如果多个线程同时去初始化同一个类,那么只会有其中一个线程执行这个类的()方法,其他线程则会阻塞等待活动线程执行完毕。如果在一个类的()方法中由耗时很长的操作,那就可能造成多个进程阻塞。例如:

public class DeadLoopClass {static {if (true){ //不加if语句,编译器会提示“初始化程序必须能够正常完成”System.out.println(Thread.currentThread()+" init DeadLoopClass");while (true){}}}public static void main(String[] args) {Runnable s=new Runnable() {@Overridepublic void run() {System.out.println(Thread.currentThread()+" start");DeadLoopClass deadLoopClass=new DeadLoopClass();System.out.println(Thread.currentThread()+" run over");}};Thread thread = new Thread(s);Thread thread1 = new Thread(s);thread.start();thread1.start();}
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1412204.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

游戏辅助 -- 某游戏一键端配置

游戏一键端下载地址及安装视频: https://pan.quark.cn/s/e6a373d94707 ​https://pan.quark.cn/s/ef7ab0c48776 准备工作 Vmware虚拟机软件:用于创建和管理虚拟机。 SecureCRT:一款支持SSH的终端仿真程序,用于远程登陆服务器…

linux的寻找文件

题目 介绍 有一个非常重要的文件 sources.list)但是你忘了 它在哪了,你依稀记得它在 /etc/ 目录下,现在要你 把这个文件找出来,然后设置成自己(shiyanlou 用 户)可以访问,但是其他用户并不能访问。 目标 1.找到 sources.list 文件…

matlab使用教程(62)—matlab中的TeX 标记

您可以使用 TeX 标记向图中添加包含希腊字母和特殊字符的文本。此外,还可以使用 TeX 标记添加上标、下标以及修改文本类型和颜色。默认情况下,MATLAB 支持一部分 TeX 标记。要使用其他特殊字符,如积分和求和符号,可以改用 LaTeX 标…

Java中的ThreadLocal为什么使用弱引用

ThreadLocal中为什么使用弱引用 补个概念: ThreadLocalMap中的key就是Entry,Entry是一个弱引用,关联了当前ThreadLocal对象。需要存储的数据为值。调用set方法要传入两个参数ThreadLocal对象和要存入ThreadLocal对象的数据。 如下图&#xf…

数组邻接表+堆优化版dijkstra+蓝桥杯2022年第十三届决赛真题-出差

文章目录 邻接表数组实现堆优化版dijkstra蓝桥杯2022年第十三届决赛真题-出差 邻接表数组实现 idx是每条边的地址e保存终点的节点值w保存每条边的权值ne[idx]保存边表,idx的下一个顶点的地址h[a]保存顶点表,a是起点,h[a]是终点的地址 int e…

Java 码农失业,有没有其他出路?

本人知乎账号同公众号:老胡聊Java,欢迎留言并咨询 如果是本科学历,30岁失业,真不用慌,别盲目转行,也别盲目继续在小公司之间跳,可以找机会进大中公司。如果是35岁,本文给出的方法还应…

如何使用DEEPL免费翻译PDF

如何使用DEEPL免费翻译PDF 安装DEEPL标题取消PDF限制 安装DEEPL 安装教程比较多,这里不重复。 把英文pdf拖进去,点翻译,在下面的框中有已经翻译完毕的文档。 但是存在两个问题 问题1:这些文档是加密的。 问题2:带有De…

Vue CLI脚手架项目目录和运行流程介绍

目录 一、项目目录介绍 二、运行流程介绍 一、项目目录介绍 二、运行流程介绍 项目在启动时会先运行main.js main.js的核心代码如下 1.导入Vue import Vue from vue 2.导入App.vue import App from ./App.vue 3.实例化Vue,将App.vue渲染到index.html容器中 new Vue({r…

【C语言专题】多组输入类介绍

【C语言小专题】多组输入类介绍 1、多组输入介绍2、多组输入相关练习 1、多组输入介绍 C语言中多组输入是指测试的时候,可能会有多组数据进行测试。接下来我们以两道经典的多组输入的题目来详细说明多组输入的用法。 2、多组输入相关练习 练习1:你是天…

Linux(openEuler、CentOS8)常用的IP修改方式(文本配置工具nmtui+配置文件+nmcli命令)

----本实验环境为openEuler系统<以server方式安装>&#xff08;CentOS类似&#xff0c;可参考本文&#xff09;---- 一、知识点 &#xff08;一&#xff09;文本配置工具nmtui(openEuler已预装) nmtui&#xff08;NetworkManager Text User Interface&#xff09;是一…

基于GWO灰狼优化的CNN-LSTM-Attention的时间序列回归预测matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1卷积神经网络&#xff08;CNN&#xff09;在时间序列中的应用 4.2 长短时记忆网络&#xff08;LSTM&#xff09;处理序列依赖关系 4.3 注意力机制&#xff08;Attention&#xff09; 4…

软考 系统架构设计师系列知识点之软件可靠性基础知识(9)

接前一篇文章&#xff1a;软考 系统架构设计师系列知识点之软件可靠性基础知识&#xff08;8&#xff09; 所属章节&#xff1a; 第9章. 软件可靠性基础知识 第2节 软件可靠性建模 9.2.2 软件可靠性的建模方法 一个软件可靠性模型通常&#xff08;但不是绝对&#xff09;由以…

Java 三大特性之继承

目录 一、为什么需要继承&#xff1f; 二、继承概念 三、继承的语法 四、子类访问父类成员 五、super关键字 六、子类构造方法的调用顺序 七、继承关系下的初始化 八、继承的三种方式 九、final关键字 十、继承和组合 一、为什么需要继承&#xff1f; 比如狗和猫&a…

IP 地理定位神话与事实

ip地理定位是一项技术&#xff0c;用于通过访问设备的ip地址来获取地理位置信息&#xff0c;例如国家、城市、经纬度等。该技术广泛应用于网站内容自定义、广告定位、网络安全和用户分析等领域。它通过与包含ip地址和地理位置映射的大型数据库进行查询来工作&#xff0c;但在准…

一个适合初学者入门机器人建模、控制和仿真的demo项目,代码已开源!

最近整理了一下自己带新同学做机器人领域入门学习时需要完成的一个工程练习demo&#xff0c;在此分享&#xff0c;可以作为机器人领域的初学者们学习机器人的运动学建模、轨迹规划以及机器人仿真&#xff08;基于CoppeliaSim&#xff09;的入门教程。 demo的代码已开源&#xf…

数仓开发:DIM层数据处理

一、了解DIM层 这个就是数仓开发的分层架构 我们现在是在DIM层&#xff0c;从ods表中数据进行加工处理&#xff0c;导入到dwd层&#xff0c;但是记住我们依然是在DIM层&#xff0c;而非是上面的ODS和DWD层。 二、处理维度表数据 ①先确认hive的配置 -- 开启动态分区方案 -- …

ThreeJS:常见几何体与基础材质入门

在前文《ThreeJS:Geometry与顶点|索引|面》中&#xff0c;我们了解了与Geometry几何体相关的基础概念&#xff0c;也尝试了如何通过BufferGeometry自定义几何体。 常见Geometry几何体 ThreeJS内部也提供了诸多封装好的几何体&#xff0c;常见的Geometry几何体如下图所示&#…

volatile 和 synchronzied 的区别

文章目录 概述volatilesynchornizedvolatile vs synchornized总结 概述 提起并发编程&#xff0c;我们不得不说起 volatile 和 synchronized 这两个关键字&#xff0c;这两个关键字也是面试中常常被问到的&#xff0c;下面我们分别介绍一下这两个关键字以及二者的异同。首先需要…

Qt与MySQL连接

QT连接Mysql数据库&#xff08;详细成功版&#xff09;-CSD N博客 我的MySQL是64位的&#xff0c;所以我的Qt的套件也需要是64位的 遇到的问题&#xff1a; &#xff08;available drivers中已经有QMYSQL QMYSQL3&#xff0c;还是not loaded&#xff09; QSqlDatabase: QMYS…