类加载的过程

2023-10-28

1 加载

注意:“加载”是“类加载”(Class Loading)过程的第一步

1.1 加载的过程

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

  • 通过一个类的全限定名来获取定义此类的二进制字节流(class文件)
    在程序运行过程中,当要访问一个类时,若发现这个类尚未被加载,并满足类初始化的条件时,就根据要被初始化的这个类的全限定名找到该类的二进制字节流,开始加载过程
  • 将这个字节流的静态存储结构转化为方法区的运行时数据结构
  • 在内存中创建一个该类的java.lang.Class对象,作为方法区该类的各种数据的访问入口

程序在运行中所有对该类的访问都通过这个类对象,也就是这个Class对象是提供给外界访问该类的接口

1.2 从哪里加载

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

  • 从ZIP包中读取
    Jar、War、Ear等
  • 其它文件生成
    由JSP文件中生成对应的Class类.
  • 从数据库中读取
    将二进制字节流存储至数据库中,然后在加载时从数据库中读取.有些中间件会这么做,用来实现代码在集群间分发.
  • 从网络中获取
    从网络中获取二进制字节流.典型就是Applet.
  • 运行时计算生成
    动态代理技术,用PRoxyGenerator.generateProxyClass为特定接口生成形式为"*$Proxy"的代理类的二进制字节流.

1.3 类和数组加载过程的区别

数组也有类型,称为“数组类型”.如:
String[] str = new String[10];
这个数组的数组类型是Ljava.lang.String,而String只是这个数组的元素类型.

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

而普通类的加载由类加载器创建.既可以使用系统提供的引导类加载器,也可以由用户自定义的类加载器完成(即重写一个类加载器的loadClass()方法).

1.4 加载过程的注意点

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

2 验证

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

2.1 验证的目的

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

2.2 为什么需要验证

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

但是我们知道,编译器和虚拟机是两个独立的东西,虚拟机只认二进制字节流,它不会管所获得的二进制字节流是哪来的,当然,如果是编译器给它的,那么就相对安全,但如果是从其它途径获得的,那么无法确保该二进制字节流是安全的。

通过上文可知,虚拟机规范中没有限制二进制字节流的来源,在字节码层面上,上述Java代码无法做到的都是可以实现的,至少语义上是可以表达出来的,为了防止字节流中有安全问题,需要验证!

2.3 验证的过程

  • 文件格式验证
    验证字节流是否符合Class文件格式的规范,并且能被当前的虚拟机处理.
    本验证阶段是基于二进制字节流进行的,只有通过了本阶段的验证,才会被允许存入到方法区中存储.
    后面的三个验证阶段都是基于方法区的存储结构进行,不会再直接操作字节流.

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

  • 元数据验证
    对字节码描述信息进行语义分析,确保符合Java语法规范.
  • 字节码验证
    本阶段是验证过程的最复杂的一个阶段.
    本阶段对方法体进行语义分析,保证方法在运行时不会出现危害虚拟机的事件.
    字节码验证将对类的方法进行校验分析,保证被校验的方法在运行时不会做出危害虚拟机的事,一个类方法体的字节码没有通过字节码验证,那一定有问题,但若一个方法通过了验证,也不能说明它一定安全
  • 符号引用验证
    发生在JVM将符号引用转化为直接引用的时候,这个转化动作发生在解析阶段,对类自身以外的信息进行匹配校验,确保解析能正常执行.

3 准备

准备阶段完成两件事情:

  • 为已在方法区中的类的静态成员变量分配内存
  • 为静态成员变量设置初始值
    初始值为0、false、null等


public static final int value = 123;

准备阶段后 a 的值为 0,而不是 123,要在初始化之后才变为 123,但若被final修饰的常量如果有初始值,那么在编译阶段就会将初始值存入constantValue属性中,在准备阶段就将constantValue的值赋给该字段(此处将value赋为123).

4 解析

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

5 初始化

真正开始执行类中定义的Java程序代码(或者说是字节码)
初始化阶段就是执行类构造器clinit()的过程.

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

初始化过程的注意点

  • clinit()方法是IDE自动收集类中所有类变量的赋值动作和静态语句块中的语句合并产生的,IDE收集的顺序是由语句在源文件中出现的顺序所决定的.
  • 静态代码块只能访问到出现在静态代码块之前的变量,定义在它之后的变量,在前面的静态语句块可以赋值,但是不能访问.
public class Test {
    static {
        i=0;
        System.out.println(i);//编译失败:"非法向前引用"
    }
    static int i = 1;
}
  • 实例构造器init()需要显示调用父类构造函数,而类的clinit()不需要调用父类的类构造函数,虚拟机会确保子类的clinit()方法执行前已经执行完毕父类的clinit()方法.因此在JVM中第一个被执行的clinit()方法的类肯定是java.lang.Object.
  • 如果一个类/接口中没有静态代码块,也没有静态成员变量的赋值操作,那么编译器就不会为此类生成clinit()方法.
  • 接口也需要通过clinit()方法为接口中定义的静态成员变量显示初始化。
  • 接口中不能使用静态代码块,但仍然有变量初始化的赋值操作,因此接口与类一样都会生成clinit()方法.不同的是,执行接口的clinit()方法不需要先执行父接口的clinit()方法.只有当父接口中的静态成员变量被使用到时才会执行父接口的clinit()方法.
  • 虚拟机会保证在多线程环境中一个类的clinit()方法别正确地加锁,同步.当多条线程同时去初始化一个类时,只会有一个线程去执行该类的clinit()方法,其它线程都被阻塞等待,直到活动线程执行clinit()方法完毕.

其他线程虽会被阻塞,只要有一个clinit()方法执行完,其它线程唤醒后不会再进入clinit()方法.同一个类加载器下,一个类型只会初始化一次.

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

类加载的过程 的相关文章

随机推荐

  • 机器学习之监督学习

    目录 1 监督学习 1 1 分类与回归 1 2 泛化 过拟合与欠拟合 1 3 监督学习算法 1 3 1 KNN K Nearest Neighbor 1 3 2 线性模型 1 3 3 决策树 随机森林 梯度提升回归树 梯度提升机 1 监督学
  • 【SPSS】百分位数计算方法探讨:SPSS计算差异

    由一道课后题引发的思考 节约时间 先说结论 在数据量大时各个方法得到的值差距不大 在数据量较小时推荐使用 三 方法4 也就是SPSS的默认方法 本周课程作业有一道非常简单 虽然事实证明此事并不简单 的题目 见下 计算下列数据的第32位百分数
  • lombok的使用和原理

    版权声明 本文为博主原创文章 未经博主允许不得转载 https blog csdn net DSLZTX article details 46715803 一 项目背景 在写Java程序的时候经常会遇到如下情形 新建了一个Class类 然后
  • 学习统计学之方差分析

    方差分析是用来研究诸多控制变量中哪些变量对观测变量的变化有显著性性的影响 对观测变量有显著性影响的各个控制变量的不同水平以及各个水平的交互搭配是如何影响观测变量的 方差分析认为观测变量值的变化是有控制变量的不同水平和随机因素影响的 如果控制
  • QT 通过鼠标拖动实现框选

    功能 QT 实现框选 QT 通过鼠标拖动实现框选 实现移动鼠标时 能够进行框选 需要三个鼠标事件 一个画图事件 用来画长方形 protected void mousePressEvent QMouseEvent event void mou
  • 自定义WPF面板

    WPF拥有很多布局面板 比如Grid StackPanel等 这让我们的界面布局更加的灵活 但也许这些面板并不能完全满足你的需求 这时你需要自定义面板来满足你的布局要求 1 面板布局是如何实现的 WPF布局引擎采用了一种递归的方式来实现控件
  • springboot项目打包时提示“程序包xxx不存在,找不到符号”

    springboot多包工程 在使用maven进行打包的时候 一直提示 程序包xxx不存在 找不到符号 但项目是可以正常启动运行的 开始一直以为是maven哪里没有配置好导致的 后来才发现原来是springboot的maven插件导致的 s
  • python: create animated GIF

    在Pillow包中提供了9种不同的图片模式 分别是1 L P RGBYCbCr I F 模式 说明 l 二值图像 只有黑 白两种颜色 L 灰度图像 P 8位色彩图像 RGB 红 绿 蓝色彩空间 每一种颜色值在0 255内 RGBA 对图像增
  • Linux 中的 col 命令及示例

    Linux 系统中的col命令用于过滤掉反向换行 使输出看起来更加正确 只有正向和半正向换行 并尽可能用制表符替换空白字符 这在处理 nroff 和 tbl 的输出时被证明是有用的 col 实用程序只是从标准输入读取并写入标准输出 句法 列
  • HBASE Phoenix异步创建索引报错

    hbase phoenix异步创建索引报错 使用 hbase phoenix异步创建索引报错 错误信息如下 Error ERROR 102 08001 Malformed connection url ERROR 102 08001 ERR
  • android view初始化 开线程,Android框架保证View更新必须在主线程的解读

    今天一位朋友问了我一个问题 android更新ui的时候 如果不在主线程更新ui 系统就会报出错误 应用崩溃 CalledFromWrongThreadException Only the original thread that crea
  • jupyter notebook文件默认存储位置更改

    引用自https blog csdn net Asabc12345 article details 105856044 https blog csdn net qq 24982339 article details 111321852 目录
  • 【星海出品】ansible入门(一)

    1 安装ansible 1 1安装 sudo apt get install y ansible 1 2 配置时需要生成秘钥 ssh keygen 推荐秘钥 1 3 推送秘钥 ssh copy id 10 0 0 7 ssh copy id
  • UML时序图总结

    UML时序图总结 时序图简介 首先 时序图用来表示用例中的行为顺序 当执行一个用例行为时 顺序图中的每条消息对应了一个类操作或者状态机中引起转换的事件 其次 时序图展示对象之间的交互 这些交互是指在场景或用例的事件流中发生的 时序图属于动态
  • SVN切换账户

    前言 一般我们都是将SVN的账户和密码保存起来 每次下载东西时都是默认登录去下载 这样十分方便 但是有时候需要切换账户去下载一些东西 这时候就涉及到切换账户 大多数时候我们都有自己的专用电脑 很少切换账户 对切换账户不是很熟悉 在这里记录一
  • 【计算机视觉

    文章目录 一 分割 语义相关 13篇 1 1 Semantic and Articulated Pedestrian Sensing Onboard a Moving Vehicle 1 2 360 circ from a Single
  • 学python要有多少英语词汇量_英语词汇量到底多大才够?

    1986年 英国 卫报 估算英国人2岁的单词量约为300个 5岁时为5000个 到了12岁 词汇量在12000个左右 卫报 的研究认为大多数人之后的词汇量都不会有太大的变化 它还指出 12000词汇量基本等同于流行报纸每天文章里的词汇量 但
  • 七天搞定Node.js微信公众号

    课程介绍 微信公众号已经1000多万个了 即使不为市场 为自己也应该学会开发它 环境参数 技术语言 Node js 框架 Node js gt 0 12 Koa1 2 0 课程所需开发系统 不限 编译环境 不限 数据库 Mongodb gt
  • 关于静态和动态代码块

    静态代码块 static 实例代码块 静态代码块相当于静态方法 实例代码块相当于实例方法 实例方法在静态方法后面执行 一个对象时 静态代码块和实例代码块只执行一次 当有多个对象时 静态代码块只执行一次 因为静态代码块是属于类的 在将clas
  • 类加载的过程

    1 加载 注意 加载 是 类加载 Class Loading 过程的第一步 1 1 加载的过程 在加载过程中 JVM主要做3件事情 通过一个类的全限定名来获取定义此类的二进制字节流 class文件 在程序运行过程中 当要访问一个类时 若发现