类加载时机与过程 asdfsadfasdfsa 09-19 268

简介: 版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34173549/article/details/79612719  通过之前的介绍可知,类加载过程共有5个步骤,分别是:加载、验证、准备、解析、初始化。
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_34173549/article/details/79612719
 

通过之前的介绍可知,类加载过程共有5个步骤,分别是:加载、验证、准备、解析、初始化。其中,验证、准备、解析称为连接。下面详细介绍这5个过程JVM所做的工作。

加载

注意:“加载”是“类加载”过程的第一步,千万不要混淆。

1. 加载的过程

在加载过程中,JVM主要做3件事情:

  1. 通过一个类的全限定名来获取这个类的二进制字节流,即class文件: 
    在程序运行过程中,当要访问一个类时,若发现这个类尚未被加载,并满足类初始化时机的条件时,就根据要被初始化的这个类的全限定名找到该类的二进制字节流,开始加载过程。
  2. 将二进制字节流的存储结构转化为特定的数据结构,存储在方法区中;
  3. 在内存中创建一个java.lang.Class类型的对象: 
    接下来程序在运行过程中所有对该类的访问都通过这个类对象,也就是这个Class类型的类对象是提供给外界访问该类的接口。

2. 从哪里加载?

JVM规范对于加载过程给予了较大的宽松度。一般二进制字节流都从已经编译好的本地class文件中读取,此外还可以从以下地方读取:

  • 从压缩包中读取 
    如:Jar、War、Ear等。
  • 从其它文件中动态生成 
    如:从JSP文件中生成Class类。
  • 从数据库中读取 
    将二进制字节流存储至数据库中,然后在加载时从数据库中读取。有些中间件会这么做,用来实现代码在集群间分发。
  • 从网络中获取 
    从网络中获取二进制字节流。典型就是Applet。

3. 类 和 数组加载过程的区别?

数组也有类型,称为“数组类型”。如:

String[] str = new String[10];
  • 1

这个数组的数组类型是Ljava.lang.String,而String只是这个数组中元素的类型。

当程序在运行过程中遇到new关键字创建一个数组时,由JVM直接创建数组类,再由类加载器创建数组中的元素类。

而普通类的加载由类加载器完成。既可以使用系统提供的引导类加载器,也可以使用用户自定义的类加载器。

4. 加载过程的注意点

  1. JVM规范并未给出类在方法区中存放的数据结构 
    类完成加载后,二进制字节流就以特定的数据结构存储在方法区中,但存储的数据结构是由虚拟机自己定义的,JVM规范并没有指定。
  2. JVM规范并没有指定Class对象存放的位置 
    在二进制字节流以特定格式存储在方法区后,JVM会创建一个java.lang.Class类型的对象,作为本类的外部接口。既然是对象就应该存放在堆内存中,不过JVM规范并没有给出限制,不同的虚拟机根据自己的需求存放这个对象。HotSpot将Class对象存放在方法区。
  3. 加载阶段和连接阶段是交叉的 
    通过之前的介绍可知,类加载过程中每个步骤的开始顺序都有严格限制,但每个步骤的结束顺序没有限制。也就是说,类加载过程中,必须按照如下顺序开始: 
    加载、连接、初始化,但结束顺序无所谓,因此由于每个步骤处理时间的长短不一就会导致有些步骤会出现交叉。 

验证

验证阶段比较耗时,它非常重要但不一定必要,如果所运行的代码已经被反复使用和验证过,那么可以使用-Xverify:none参数关闭,以缩短类加载时间。

1. 验证的目的是什么?

验证是为了保证二进制字节流中的信息符合虚拟机规范,并没有安全问题。

2. 为什么需要验证?

虽然Java语言是一门安全的语言,它能确保程序猿无法访问数组边界以外的内存、避免让一个对象转换成任意类型、避免跳转到不存在的代码行,如果出现这些情况,编译无法通过。也就是说,Java语言的安全性是通过编译器来保证的。

但是我们知道,编译器和虚拟机是两个独立的东西,虚拟机只认二进制字节流,它不会管所获得的二进制字节流是哪来的,当然,如果是编译器给它的,那么就相对安全,但如果是从其它途径获得的,那么无法确保该二进制字节流是安全的。通过上文可知,虚拟机规范中没有限制二进制字节流的来源,那么任意来源的二进制字节流虚拟机都能接受,为了防止字节流中有安全问题,因此需要验证!

3. 验证的过程

  1. 文件格式验证 
    这个阶段主要验证输入的二进制字节流是否符合class文件结构的规范。二进制字节流只有通过了本阶段的验证,才会被允许存入到方法区中。 
    本验证阶段是基于二进制字节流的,而后面的三个验证阶段都是在方法区中进行,并基于类特定的数据结构的。 

    通过上文可知,加载开始前,二进制字节流还没进方法区,而加载完成后,二进制字节流已经存入方法区。而在文件格式验证前,二进制字节流尚未进入方法区,文件格式验证通过之后才进入方法区。也就是说,加载开始后,立即启动了文件格式验证,本阶段验证通过后,二进制字节流被转换成特定数据结构存储至方法区中,继而开始下阶段的验证和创建Class对象等操作。这个过程印证了:加载和验证是交叉进行的。

  2. 元数据验证 
    本阶段对方法区中的字节码描述信息进行语义分析,确保其符合Java语法规范。

  3. 字节码验证 
    本阶段是验证过程的最复杂的一个阶段。 
    本阶段对方法体进行语义分析,保证方法在运行时不会出现危害虚拟机的事件。

  4. 符号引用验证 
    本阶段验证发生在解析阶段,确保解析能正常执行。 

准备

准备阶段完成两件事情: 
1. 为已经在方法区中的类中的静态成员变量分配内存 
类的静态成员变量也存储在方法区中。 
2. 为静态成员变量设置初始值 
初始值为0、false、null等。

示例1:

public static String name = "柴毛毛";
  • 1

在准备阶段,JVM会在方法区中为name分配内存空间,并赋上初始值null。 
给name赋上”柴毛毛”是在初始化阶段完成的。

示例2:

public static final String name = "柴毛毛";
  • 1

被final修饰的常量如果有初始值,那么在编译阶段就会将初始值存入constantValue属性中,在准备阶段就将constantValue的值赋给该字段。 

解析

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

初始化

初始化阶段就是执行类构造器clinit()的过程。 
clinit()方法由编译器自动产生,收集类中static{}代码块中的类变量赋值语句和类中静态成员变量的赋值语句。在准备阶段,类中静态成员变量已经完成了默认初始化,而在初始化阶段,clinit()方法对静态成员变量进行显示初始化。

初始化过程的注意点

  1. clinit()方法中静态成员变量的赋值顺序是根据Java代码中成员变量的出现的顺序决定的。
  2. 静态代码块能访问出现在静态代码块之前的静态成员变量,无法访问出现在静态代码块之后的成员变量。
  3. 静态代码块能给出现在静态代码块之后的静态成员变量赋值。
  4. 构造函数init()需要显示调用父类构造函数,而类的构造函数clinit()不需要调用父类的类构造函数,因为虚拟机会确保子类的clinit()方法执行前已经执行了父类的clinit()方法。
  5. 如果一个类/接口中没有静态代码块,也没有静态成员变量的赋值操作,那么编译器就不会生成clinit()方法。
  6. 接口也需要通过clinit()方法为接口中定义的静态成员变量显示初始化。
  7. 接口中不能使用静态代码块。
  8. 接口在执行clinit()方法前,虚拟机不会确保其父接口的clinit()方法被执行,只有当父接口中的静态成员变量被使用到时才会执行父接口的clinit()方法。
  9. 虚拟机会给clinit()方法加锁,因此当多条线程同时执行某一个类的clinit()方法时,只有一个方法会被执行,其它的方法都被阻塞。并且,只要有一个clinit()方法执行完,其它的clinit()方法就不会再被执行。因此,在同一个类加载器下,同一个类只会被初始化一次。

相关文章
|
供应链 前端开发 算法
技术人应该知道的电商运营小知识(上)
技术人应该知道的电商运营小知识(上)
873 0
|
9月前
|
人工智能
如何筛选高流量的AI模型?
我会定期查看huggingface.co/spaces和replicate.com排行榜,关注优质博主以了解最新实用模型。例如,根据排行榜趋势,我用两天时间创建了[grokghibli](https://grokghibli.pro/)。不过,自定义驱动视频失败率较高,模型还需优化。如有产品改进建议,请随时告诉我!期待与大家交流探讨。
|
机器学习/深度学习 人工智能 自然语言处理
LTX Video:Lightricks推出的开源AI视频生成模型
LTX Video是由Lightricks推出的开源AI视频生成模型,能够在4秒内生成5秒的高质量视频。该模型基于2亿参数的DiT架构,确保帧间平滑运动和结构一致性,支持长视频制作,适用于多种场景,如游戏图形升级和电子商务广告变体制作。
1071 1
LTX Video:Lightricks推出的开源AI视频生成模型
|
安全 Linux 开发者
Windows系统历史版本简介
Windows系统历史版本简介
766 0
|
存储 算法 Serverless
LabVIEWCompactRIO 开发指南31 在LabVIEW FPGA中使用DMA FIFO
LabVIEWCompactRIO 开发指南31 在LabVIEW FPGA中使用DMA FIFO
274 0
|
供应链 安全 IDE
重磅发布|OpenSCA SaaS全新上线
OpenSCA SaaS全新上线!
281 0
|
安全 测试技术 网络安全
2024年甘肃省职业院校技能大赛中职组“网络安全”赛项竞赛样题-B模块安全事件响应/网络安全数据取证/应用安全
该内容描述了一组网络安全相关的挑战,涉及Windows渗透测试、数字取证、Web安全和跨站脚本渗透。挑战包括使用Kali Linux对服务器进行扫描,提取特定服务信息、DNS、管理员密码、文件名和内容等。此外,需要分析pcapng数据包文件,找出恶意HTTP访问、扫描端口、密码和木马信息。还有Web渗透测试,需访问不同URL并解决提示以获取Flag。最后,涉及跨站脚本攻击,需在不同目录下满足条件获取弹框信息。每个部分都要求参与者找出特定的安全标志物(Flag)。
268 0
|
存储 SQL Java
实战 | 使用Spring Boot + Elasticsearch + Logstash 实现图书查询检索服务
前面我们介绍了Spring Boot 整合 Elasticsearch 实现数据查询检索的功能,在实际项目中,我们的数据一般存储在数据库中,而且随着业务的发送,数据也会随时变化。 那么如何保证数据库中的数据与Elasticsearch存储的索引数据保持一致呢? 最原始的方案就是:当数据发生增删改操作时同步更新Elasticsearch。但是这样的设计耦合太高。接下来我们介绍一种非常简单的数据同步方式:Logstash 数据同步。
实战 | 使用Spring Boot + Elasticsearch + Logstash 实现图书查询检索服务
|
弹性计算 固态存储 数据可视化
阿里云服务器价格表2023最新版CPU内存带宽报价
阿里云服务器s6公网带宽可选1M到5M,系统盘40G起可选高效云盘、SSD云盘或ESSD云盘,1核1G配置19.17元3个月、306.72元一年,1核2G优惠价26.46元3个月、423.36元一年,2核4G配置42.66元3个月,2核8G配置58.86元3个月,4核8G 75.06元3个月,8核16G 139.86元3个月,还有4核16G、8核32G多配置可选。不只是云服务器ECS共享型s6实例,ECS计算型c6、通用型g6、内存型r6、GPU服务器
538 0
阿里云服务器价格表2023最新版CPU内存带宽报价
|
测试技术 C语言 C++
PTA团体程序设计天梯赛-练习集:L1-003 个位数统计
给定一个 k 位整数 N=dk−110k−1+⋯+d1101+d0 (0≤di≤9, i=0,⋯,k−1, dk−1>0),请编写程序统计每种不同的个位数字出现的次数。例如:给定 N=100311,则有 2 个 0,3 个 1,和 1 个 3。 输入格式: 每个输入包含 1 个测试用例,即一个不超过 1000 位的正整数 N。 输出格式: 对 N 中每一种不同的个位数字,以 D:M 的格式在一行中输出该位数字 D 及其在 N 中出现的次数 M。要求按 D 的升序输出。
444 0

热门文章

最新文章