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.mzph.cn/diannao/6773.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

使用socket+Python实现ping

import os import socket import struct import select import time# 计算校验和&#xff0c;用于确保数据的完整性 def checksum(source_string):sum 0count 0max_count len(source_string)# 处理成对的字节while count < max_count - 1:val source_string[count 1] *…

5.5代码

目录 1.内存空间 1.内存空间 真的要吐了&#xff0c;人都麻了题还没看完&#xff0c;看样子就是要想办法提取出来想要的东西2022第十三届蓝桥杯决赛C/C大学A组-C题内存空间_蓝桥杯a组c语言题目-CSDN博客 这个是一个非常清晰的代码&#xff0c;好几个帖子都管这个题叫大模拟题…

开源版本管理系统的搭建二:SVN部署及使用

作者&#xff1a;私语茶馆 1. Visual SVN Server部署 SVN Server部署包括&#xff1a; 创建版本仓库创建用户 这些部署是通过VisualSVN Server Manager实现的&#xff0c;如下图&#xff1a; VisualSVN Server Manager&#xff08;安装后自带&#xff09; 1.1.SVN 初始化配…

【QEMU系统分析之实例篇(十五)】

系列文章目录 第十五章 QEMU系统仿真的机器创建分析实例 文章目录 系列文章目录第十五章 QEMU系统仿真的机器创建分析实例 前言一、QEMU是什么&#xff1f;二、QEMU系统仿真的机器创建分析实例1.系统仿真的命令行参数2.应用旧的机器设置项qemu_apply_legacy_machine_options()…

ROS2学习——Docker环境下安装于使用(1)

目录 一、简要 二、ROS2和ROS1区别 三、环境搭建与安装 &#xff08;2&#xff09;拉取ubuntu22.04镜像 &#xff08;2&#xff09;安装ROS2 1. 基本设置 2.设置源 3.安装ROS2功能包 4.测试 四、相关指令学习 1.小海龟测试 2.ros2 node等指令 3.rqt 一、简要 随着R…

Git常用(持续更新)

常用场景&#xff1a; 初始化&#xff1a; git config --global user.name "codelabs" git config --global user.email mycodelabs.com git init git remote add origin https://github.com/username/repository.git git pull origin master 提交&#xff1a; gi…

踏春正当时!VELO Prevail Ride带你探索多元骑行潮流体验~

嘿&#xff0c;朋友&#xff01;踏春正当时嘞&#xff01;在这个追求个性化与多元化的新时代&#xff0c;骑行爱好者们也开始寻找能适应各种骑行场景的理想坐垫。从悠闲自在的日常通勤&#xff0c;到热血沸腾的公路竞速&#xff0c;再到勇攀高峰的山地探险&#xff0c;维乐VELO…

HNU-人工智能-实验3-贝叶斯分类器

人工智能-实验3 计科210x 甘晴void 【感悟】本实验值得自己完成一遍 文章目录 人工智能-实验3一、实验目的二、实验平台三、实验内容3.0 基础知识3.1 条件概率&#xff08;选择题&#xff09;3.2 贝叶斯公式&#xff08;选择题&#xff09;3.3 朴素贝叶斯分类算法流程3.3.1 算…

多线程系列(三) -synchronized 关键字使用详解

一、简介 在之前的线程系列文章中&#xff0c;我们介绍了线程创建的几种方式以及常用的方法介绍。 今天我们接着聊聊多线程线程安全的问题&#xff0c;以及解决办法。 实际上&#xff0c;在多线程环境中&#xff0c;难免会出现多个线程对一个对象的实例变量进行同时访问和操…

3-qt综合实例-贪吃蛇的游戏程序

引言&#xff1a; 如题&#xff0c;本次实践课程主要讲解贪吃蛇游戏程序。 qt贪吃蛇项目内容&#xff1a; 一、功能需求 二、界面设计 各组件使用&#xff1a; 对象名 类 说明 Widget QWidge 主窗体 btnRank QPushButton 排行榜-按钮 groupBox QGroupBox 难…

【数据结构与算法】力扣 347. 前 K 个高频元素

题目描述 给你一个整数数组 nums 和一个整数 k &#xff0c;请你返回其中出现频率前 k 高的元素。你可以按 任意顺序 返回答案。 示例 1: 输入: nums [1,1,1,2,2,3], k 2 输出: [1,2]示例 2: 输入: nums [1], k 1 输出: [1]提示&#xff1a; 1 < nums.length < …

python数据分析——数据分析的统计推断

数据分析的统计推断 前言一、提出问题二、统计归纳方法三、统计推断四、统计推断步骤4.1.点估计4.2.区间估计4.2.1. 总体方差已知4.2.2总体方差未知 4.3. 假设检验4.4. 假设检验的假设4.5.显著性水平 五、检验统计量六、检验方法七、拒绝域八、假设检验步骤九、重要假设检验方法…

伺服电机初识

目录 一、伺服电机的介绍二、伺服电机的基本原理三、伺服电机的技术特点四、伺服电机的分类五、实际产品介绍1、基本技术规格&#xff1a;2、MD42电机硬件接口3、通讯协议介绍3.1 通讯控制速度运行3.2 通讯控制位置运行3.3 通讯控制转矩运行 4、状态灯与报警信息 一、伺服电机的…

金融行业AI大模型百项应用案例综述【大模型系列】

逐浪金融大模型的玩家&#xff0c;除了BAT、华为等高科技巨头&#xff0c;试图以技术优势充当产业链的“卖铲人”&#xff0c;更多的还是金融和类金融企业&#xff0c;包括银行、保险、互金、券商等&#xff0c;既不想被喧宾夺主&#xff0c;又不想肥水外流&#xff0c;都在押注…

确保WEB应用安全:深入分析与有效防范

目录 前言 一. 概述 二. WEB应用体系结构脆弱性分析 1.前端用户界面 2.后端服务器 3.数据库 4.风险和后果 5.缓解措施 三. SQL注入攻击及防范 1.使用参数化查询或预定义语句 2.输入验证和转义 3.最小化数据库权限 4.使用存储过程和预定义语句 5.保持软件更新 6.实…

【Linux系统化学习】网络套接字(编写简单的TCP服务端和客户端)

目录 TCP服务端 创建套接字 解决绑定失败问题 填充网络信息 绑定 设置监听状态 接受请求 收取和反馈消息 完整的服务端封装代码 TCP客户端 创建套接字 填充网络信息 发起连接 发送和收取消息 客户端完整代码 一些补充 TCP服务端 初始化服务端 创建套接字 和U…

【JAVA项目】基于个人需求和地域特色的【外卖推荐系统】

技术简介&#xff1a;采用B/S架构、ssm 框架、Java技术、MySQL等技术实现。 系统简介&#xff1a;统权限按管理员&#xff0c;商家和用户这三类涉及用户划分。(a) 管理员&#xff1b;管理员使用本系统涉到的功能主要有&#xff1a;首页&#xff0c;个人中心&#xff0c;用户管理…

C++ | Leetcode C++题解之第61题旋转链表

题目&#xff1a; 题解&#xff1a; class Solution { public:ListNode* rotateRight(ListNode* head, int k) {if (k 0 || head nullptr || head->next nullptr) {return head;}int n 1;ListNode* iter head;while (iter->next ! nullptr) {iter iter->next;n…

ctfshow web入门 sql注入 web201--web208

web201 先扫描先 python .\sqlmap.py -u "http://4863661d-2371-4812-ae62-128fadbdc0a4.challenge.ctf.show/api/?id" --user-agentsqlmap 加头 python .\sqlmap.py -u "http://4863661d-2371-4812-ae62-128fadbdc0a4.challenge.ctf.show/api/?id" --u…

每日算法之从前序与中序遍历序列构造二叉树

题目描述 给定两个整数数组 preorder 和 inorder &#xff0c;其中 preorder 是二叉树的先序遍历&#xff0c; inorder 是同一棵树的中序遍历&#xff0c;请构造二叉树并返回其根节点。 示例 1: &#xfffc; 输入: preorder [3,9,20,15,7], inorder [9,3,15,20,7] 输出: […