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

news2024/11/16 8:18:07

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() {
            @Override
            public 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.coloradmin.cn/o/1644001.html

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

相关文章

3. 深度学习笔记--优化函数

深度学习——优化器算法Optimizer详解(BGD、SGD、MBGD、Momentum、Adagrad、Adadelta、RMSprop、Adam、Nadam、AdaMax、AdamW ) 0. GD (梯度下降) Gradient Descent(梯度下降)是一种迭代优化算法&#xf…

你必须要知道的P沟道MOSFET场效应管AO3401电流-4A电压-30V

概述 多年前,P沟道MOSFET是很少的,国内新兴的半导体厂家还都没开起来,这个时候出了一款SOT23封装的P沟道MOSFET,这就是AO3401,对于小电流输出的电源控制,就变的简单起来。目前国内已有多家厂商可以生产各种…

【字符串】Leetcode 最长回文子串

题目讲解 5. 最长回文子串 算法讲解 dp[i][j]表示i~j这一段区间的子串是否是回文 当s[i] s[j]的时候&#xff0c;此时是有三种情况的&#xff1a; ij说明一个字符肯定是回文 i1 j也说明一个字符是回文 i1 < j说明需要判断[i1, j-1]这一段区间是否是回文 此时我们就可以…

【每日刷题】Day32

【每日刷题】Day32 &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;每日刷题&#x1f34d; &#x1f33c;文章目录&#x1f33c; 1. 1652. 拆炸弹 - 力扣&#xff08;LeetCode&#xff09; 2. 2058. 找出临界点之间的最小和最大距离 …

深圳网站推广怎么做

深圳是中国最具活力和创新精神的城市之一&#xff0c;拥有众多的互联网企业和创新型公司。如果你是一家深圳企业&#xff0c;想要在市场上获得成功&#xff0c;互联网是不可或缺的一部分。网站推广是你在线上推销你的企业或产品的一种方式&#xff0c;让更多的人知道你&#xf…

Linux: Netfilter 简介

文章目录 1. 前言2. Netfilter 简介2.1 Netfilter 的功能2.2 Netfilter 示例2.3 Netfilter 实现概览2.3.1 Netfilter hook 的 注册 和 注销2.3.2 Netfilter hook 的触发2.3.2.1 NF_INET_PRE_ROUTING2.3.2.2 NF_INET_LOCAL_IN2.3.2.3 NF_INET_FORWARD2.3.2.4 NF_INET_LOCAL_OUT2…

八、Linux进程检测与控制

章节目标 了解进程和程序的关系了解进程的特点能够使用top动态查看进程信息能够使用ps静态查看进程信息能够使用kill命令给进程发送信号能够调整进程的优先级&#xff08;扩展&#xff09; 引言 在运维的日常工作中&#xff0c;监视系统的运行状况是每天例行的工作&#xff…

python数据分析——业务数据描述

业务数据描述 前言一、数据收集数据信息来源 二、公司内部数据&#xff08;1&#xff09;客户资料数据&#xff08;2&#xff09;销售明细数据&#xff08;3&#xff09;营销活动数据 三、市场调查数据1 观察法2 提问法3 实验法 四、公共数据五、第三方数据六、数据预处理七、数…

Vue工程化开发和脚手架Vue CLI

目录 一、介绍 二、使用步骤 1. 全局安装&#xff08;一次&#xff09; 2.查看Vue版本 3.创建项目架子&#xff08;项目名不能使用中文&#xff09; 4.启动项目 一、介绍 Vue CLI是Vue官方提供的一个全局命令工具。可以帮助我们快速创建一个开发的Vue项目的标准化基础架子…

【HCIP】OSPF综合实验报告

一、分析要求 R4为ISP&#xff0c;直连设备间使用公网网段R3、R5、R6、R7为MGRE环境&#xff0c;R3为NHS合理划分172.16.0.0/16网段做NAT配置使私网能够访问R4环回ospf优化&#xff08;汇总和特殊区域&#xff09;&#xff0c;减少计时器时间加快收敛&#xff0c;设备之间做认…

解决:mybatisplus分页查询失效,总是查询到所有数据

目录 问题描述&#xff1a;解决方案&#xff1a;1.配置mybatisplus拦截器PaginationInterceptor2.自行编写SQL语句查询 问题描述&#xff1a; 在前端使用Map<String, Object> params&#xff0c;把page&#xff0c;limit参数作为请求体传送到后端&#xff0c;但是分页查…

WWW‘24 | 课程学习CL+模仿学习IL用于ETF及商品期货交易

WWW24 | 课程学习CL模仿学习IL用于ETF及商品期货交易 原创 QuantML QuantML 2024-05-04 13:47 论文地址&#xff1a;[2311.13326] Curriculum Learning and Imitation Learning for Model-free Control on Financial Time-series (arxiv.org) 本文探讨了在金融时间序列数据上…

vulnhub靶场之FunBox-3

一.环境搭建 1.靶场描述 Boot2Root ! Easy going, but with this Funbox you have to spend a bit more time. Much more, if you stuck in good traps. But most of the traps have hints, that they are traps. If you need hints, call me on twitter: 0815R2d2 Have fun.…

C++实验五 : 类的继承 -----CUST

【题目】 1.定义person类&#xff0c;包括数据私有成员&#xff1a;姓名&#xff0c;性别&#xff1b;共用成员函数&#xff1a;带参数构造函数&#xff0c;display函数输出本类对象的所有数据成员值。 2.定义student类&#xff0c;保护继承person类&#xff1b;增加保护数据成…

从零开始:Django项目的创建与配置指南

title: 从零开始&#xff1a;Django项目的创建与配置指南 date: 2024/5/2 18:29:33 updated: 2024/5/2 18:29:33 categories: 后端开发 tags: DjangoWebDevPythonORMSecurityDeploymentOptimization Django简介&#xff1a; Django是一个开源的高级Python Web框架&#xff…

【Python项目】基于时间序列的【大气污染预测系统】

技术简介&#xff1a;使用Python技术、B/S架构、MYSQL数据库等实现。 系统简介&#xff1a;本系统的主要使用角色为普通用户和管理员用户&#xff0c;两者的功能几乎是一致的&#xff0c;但管理员用户比普通用户多了用户管理的功能&#xff0c;可以对系统内的用户进行管理。普通…

Vue的项目启动指令分析

通过Vue CLI脚手架创建的项目&#xff0c;默认的启动项目方式是 npm run serve 这里的serve是可以修改的。 在创建的项目目录中&#xff0c;找到package.json 双击打开&#xff0c;找到scripts部分 在scripts部分&#xff0c;有一个"serve"键值对&#xff0c;这里的…

如何进行Go语言的性能测试和调优?

文章目录 开篇一、性能测试1. 使用标准库中的testing包2. 使用第三方工具 二、性能调优1. 优化算法和数据结构2. 减少不必要的内存分配和垃圾回收3. 并发和并行 结尾 开篇 Go语言以其出色的性能和简洁的语法受到了广大开发者的喜爱。然而&#xff0c;在实际开发中&#xff0c;…

DML操作表的数据

一、增加数据 语法&#xff1a; INSERT [INTO] 表名 [( 列名表 )] VALUES ( 值列表 ) 1.1 插入全部字段 l 所有的字段名都写出来 INSERT INTO 表名 (字段名1, 字段名2, 字段名3…) VALUES (值1, 值2, 值3); l 不写字段名 INSERT INTO 表名 VALUES (值1, 值2, 值3…); 注&…

我这次没有蹭Oracle发布热度的原因

这次没有去蹭热度&#xff0c;原因有几个。 主观 确实是生病了&#xff0c;身体不舒服&#xff0c;那几个卷王在卷公众号的时候&#xff0c;我在床上卷成一团。 不和这几个打了鸡血的人比了。我卷了一点和他们不一样的。我节日期间看到我初中同班同学发的微博。 对这个就是我…