死磕P7: JVM类加载那些事儿,一起探知类的前世今生(一)

news2024/9/28 12:09:10

这是「死磕P7」系列第 005 篇文章,欢迎大家来跟我一起 死磕 100 天,争取在 2025 年来临之际,给自己一个交代。

前面几篇介绍了 JVM 的区域划分及垃圾收集相关的内容,只是告诉了你我有一栋豪华大酒店,大楼里有哪些房间,以及向你介绍了负责打扫卫生的几个清洁工,但是还没告诉你楼梯在哪呢,你该如何才能入住到房间呢?

今天我们就来介绍一下,Java 程序从一个 .java 文件是如何运行的,当然,依照目前的水平,还是尽量介绍的浅显易懂一点比较好。

大家比较熟知的流程是:编写 Hello.java -> 经过 javac 编译为 Hello.class 文件 -> 执行 java Hello 运行

这个过程,涉及到从源代码到字节码,最后再到机器码的过程,JVM 负责加载字节码并将其转化为机器码,绕了那么多,其实我就想说一个考点:类加载

类加载过程

类的源文件一般是 xx.java, 经过编译后得到字节码 xx.class 文件,程序在运行时,JVM 首先需要将所有 xx.class 文件加载到 JVM 中。

类从加载虚拟机内存中开始到卸载出内存为止,生命周期包括:加载、验证、准备、解析、初始化、使用、卸载。其中验证、准备、解析统称为连接(Linking)。

类加载过程是有 5 步,类的生命周期是有 7 步(多了使用和卸载)。

类的加载由类加载器完成,类加载器通常由JVM提供,这些类加载器也是所有程序运行的基础,JVM提供的类加载器通常被称为系统类加载器。

除此之外,开发者可以通过继承 ClassLoader 基类来创建自己的类加载器(后续文章会提供案例,本文先罗列概念)。

加载阶段

加载阶段,JVM 主要做了三件事情:

  • 通过一个类的完全限定名来获取定义此类的二进制字节流(文件);

  • 将该字节流所代表的静态存储结构转化为运行时数据结构;

  • 在堆内存中生成一个代表该类的 java.lang.Class 对象,作为方法区这个类的各种数据的访问入口。

二进制字节流的来源有很多,例如:从 ZIP 压缩包读取、从网络获取、运行时计算生成(动态代理)等。

静态存储结构就是指某个类或接口的静态变量还有静态方法。

稍微理解一下对象和类的概念,对象是实例化的类。

类的信息是存储在方法区中的,对象是存储在Java堆中的,类是对象的模板,对象是类的实例。

验证阶段

确保 Class 文件信息符合 JVM 规范,防止恶意代码危害虚拟机自身安全。

主要包括四种验证:文件格式验证,元数据验证,字节码验证,符号引用验证。

  • 文件格式验证:基于二进制字节流进行的文件格式验证,如版本是否兼容,编码是否是所允许的

  • 元数据验证:对类的信息进行语义校验,如一个类是否有父类,该父类是否继承了 final 修饰的类

  • 字节码验证:主要是数据流和控制流分析,确定语义合法、符合逻辑,如类型转换有效

  • 符号引用验证:发生在虚拟机将符号引用转为直接引用时,确保解析动作能正常执行,如符号引用中通过描述的完全限定名是否能找到对应的类

验证阶段虽然很重要,但并非必须执行,若程序代码已被反复使用和验证,可以考虑关闭大部分类验证,以缩短类加载的时间。

-Xverify:none

准备阶段

主要为类变量(即 static 修饰的静态变量)分配内存并设置「初始值」。

这里的初始值“通常”情况指的是类型零值,基本数据类型的零值如下:

看下面的示例:

// 经过「准备」阶段后,该初始值为 0
// 而把 value 赋值为 123 是在后面的「初始化」阶段
public static int value = 123;

注意:静态变量是被 final 修饰的字面值(就是具体的值,如123)的时候,由于静态变量字面值是在编译时(javac)确认的,准备阶段是直接赋值。

// 编译阶段将 value 生成常量值,在准备阶段虚拟机会直接将 value 赋值为 123
public static final int value = 123;

解析阶段

这个阶段的主要任务是将其在常量池中的符号引用替换成在内存中的直接引用。

符号引用,看例子吧:

public class A {
}

public class B {
  private A a;
}

其中 B 持有对 A 的引用,但此时两个类并未加载到内存中,仅仅是一个标记而已。

直接引用就是能够直接在内存中找到相应对象的内存地址。若有直接引用,则目标必定已在虚拟机中。

初始化阶段

初始化是为类的静态变量赋予用户声明的初始值(非基本类型的默认值),准备阶段和初始化阶段看似有点矛盾,其实是不矛盾的。

如果类中有语句:private static int a = 10,它的执行过程是这样的,首先字节码文件被加载到内存后,先进行链接的验证这一步骤,验证通过后准备阶段给a分配内存及默认值,此时a等于int类型的默认初始值0,即a=0,然后到初始化这一步骤时,才把a的真正的值10赋给a,此时a=10。

初始化阶段,才真正开始执行类中的Java程序代码,即执行类的初始化方法 clinit。

在编译生成class文件时,编译器会产生两个方法加于class文件中,一个是类的初始化方法clinit, 另一个是实例的初始化方法init。

  • clinit 指的是类构造器,主要作用是在类加载过程中的初始化阶段进行执行,执行内容包括静态变量初始化和静态块的执行,该方法并不是必需的,若类中无静态语句块和对变量的赋值操作,编译器可以不生成这个方法。。

  • init 指的是实例构造器,主要作用是在类实例化过程中执行,执行内容包括成员变量初始化和代码块的执行。

JVM 必须保证一个类的 clinit 方法在多线程环境被正确地加锁同步。如果多个线程同时去初始化一个类,只能有一个线程去执行 clinit 方法,其他线程都要阻塞等待。

设计模式的「单例模式」就有一种写法是利用该机制来保证线程安全性的,示例代码如下:

public class BeanFactory {
  private BeanFactory() {
  }

  public BeanFactory getBeanFactory() {
    return BeanFactoryHolder.beanFactory;
  }

  /**
   * 使用内部嵌套类实现单例,利用 JVM 的类加载机制可保证线程安全
   */
  private static class BeanFactoryHolder {
    private static BeanFactory beanFactory = new BeanFactory();
  }
}

使用阶段

当 JVM 完成初始化阶段之后,JVM 便开始从入口方法开始执行用户的程序代码。

这个阶段也只是了解一下就可以。

卸载阶段

最后卸载阶段,也就是程序退出,有多种情况:

  • 执行了 System.exit() 方法

  • 程序正常执行结束

  • 程序在执行过程中遇到了异常或错误而异常终止

  • 由于操作系统出现错误而导致Java虚拟机进程终止

总结

今天先分享(死磕)到这里,本文注意罗列了 JVM 类加载过程的几个步骤,分别是 加载,链接(验证,准备,解析),初始化,类的生命周期还额外的补充了 2 点(使用,卸载),至于各个阶段干了啥,大概了解一下就行,可以重点理解一下准备阶段的赋默认值和初始化阶段的赋初始值的区别。


END

好了,今天的分享就到这里,关注公&号:新质程序猿,和我一起死磕 P7, 一起学习成长。

感谢大家的阅读,如果有任何异议的地方,欢迎指正,也欢迎大家公号找到我与我做朋友!

小福利

文末小福利,作为资深囤货达人,购置或转存了上千 T 的各种资源,反正我也学不完,如有需要,可以公号: 新质程序猿 找到我,直接送您,能帮助到大家也算是有所福报吧!

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

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

相关文章

高级前端进阶:揭秘 MemFire Cloud 的强大助力

在前端开发的道路上,我们总是在追求效率与速度的平衡,如何写出优雅的代码,如何让开发流程更加顺滑成为了每个前端开发者的目标。对于那些希望提升效率、减少繁琐开发步骤的开发者来说,MemFire Cloud,一款国产的一站式应…

git忽略python项目中的__pycache__目录

一:编写.gitignore文件 # 忽略 tensorone-storage-log/ 目录 tensorone-storage-log/# 忽略 file_test 目录 file_test/# 忽略 .idea 目录 /.idea# 忽略 .vscode 目录 .vscode# 忽略 __pycache__ 目录及其所有子内容 **/__pycache__/ 二:提交更改&#x…

越来越卷,事无巨细的日报、周报难为死IT牛马们了!如何破局?

现在这环境做牛马真难呀 某中厂朋友吐槽说:“经济下行,公司要求越来越过分,不但要求9106,还要天天写日报都做了什么事项,每个事事项花了多少时间,要用不少时间来写;周报也是要求很细 还要总结&…

蜂鸟bebirdt15、西圣find、泰视朗可视挖耳勺好用吗?测评数据对比看这里

可视挖耳勺在当下已经被广泛使用,不过对于新手来说,选择一款优质产品却并不容易。蜂鸟t15、西圣find、泰视朗可视挖耳勺好用吗?作为一个测评博主,近期有不少用户问我这个问题。 根据目前市场上可视挖耳勺的品牌情况来看&#xff0…

宝塔centOs添加node环境变量

宝塔Linux添加node全局环境变量 ln -s /www/server/nodejs/v20.17.0/bin/node /usr/bin/node ln -s /www/server/nodejs/v20.17.0/bin/npm /usr/bin/npm ln -s /www/server/nodejs/v20.17.0/bin/pnpm /usr/bin/pnpm

Redis: Sorted Set 底层算法的简单分析

概述 我们先看下 Shorted Set 有序集合的内部数据结构所谓有序集合,比如有个容器,容器里边都已经排好序了,那无非就是快速的查找和插入不管你是查找还是插入,肯定要确定那个位置最简单的办法就是从最开头开始,挨个比较…

TPAMI 2024 | 数据不平衡克星,ProCo算法:长尾视觉识别的终极解决方案!

题目:长尾视觉识别的概率对比学习 作者: Chaoqun Du, Yulin Wang, Shiji Song, Gao Huang 摘要 长尾分布经常出现在现实世界的数据中,其中大量的少数类别包含有限数量的样本。这种不平衡问题极大地损害了主要为平衡训练集设计的标准监督学…

AI表情包项目变现实操,适合新手小白

做表情包还能赚钱吗?可能很多朋友会觉得这个问题很奇怪,毕竟我们平时用的表情包都是免费的。今天就带大家了解一下表情包背后的商业模式。 1、项目概况 微信表情包的售价一般在1~200元之间,最低售价是1元。可以看到网上这些表情包的销量还是…

【反素数】

题目 思路 首先分析 的性质 一定是 中约数最大的一定是约数同是最大的数字中值中最小的进一步挖掘性质,紧贴枚举的做法 约数最大值最小(也决定了层数、其它约束),是枚举的比较条件实现上述目的,枚举的质数种类在大小…

数据结构:栈 及其应用

逻辑结构: 栈(Stack)是一种遵循后进先出(LIFO, Last In First Out)原则的有序集合 (受限的线性表)。这种数据结构只允许在栈顶进行添加(push)或删除(pop&…

爆火!大模型算法岗 100 道面试题全解析,赶紧收藏!

大模型应该是目前当之无愧的最有影响力的AI技术,它正在革新各个行业,包括自然语言处理、机器翻译、内容创作和客户服务等等,正在成为未来商业环境的重要组成部分。 截至目前大模型已经超过200个,在大模型纵横的时代,不…

后端学习路线

后端学习路线 一、编程语言 至少需要学习一门编程语言,建议学习JAVA和GO语言。 二、数据库 数据库分为关系型数据库和非关系型数据库,区别在于分关系型数据库常用于大数据,而非关系型数据库一般不在大数据方面使用。 关系型数据库&#x…

重塑未来:组织文化建设助你应对时代挑战

在当今迅速变化的时代,变化已成为不可避免的常态。无论是技术的迅猛发展,还是市场需求的瞬息万变,个人和组织都必须学会适应和迎接变化。本文将探讨变化的重要性,并通过系统思维和敏捷方法论的视角,阐述如何有效地管理…

ai生产力 输出内容变现新方式 AI头像生成教程和变现方式分析

ai生产力 输出内容变现新方式 AI头像生成教程和变现方式分析:特别是以AI头像与壁纸生成为例,我们可以详细分析其制作过程和变现方式。 制作过程 选择合适的AI工具: 在市场上,存在多款AI生成图像的工具,如Stable Diff…

OJ在线评测系统 后端判题机架构搭建 使用原生实现Java安全管理器环境隔离

原生实现安全管理器环境隔离 限制用户的操作权限 文件 网络 执行 Java安全管理器 SecurityManager 来实现更严格的限制 是 Java 提供的保护 JVM Java安全的机制 可以实现更严格的资源和操作限制 编写安全管理器 只需要继承 SecurityManager类 我们可以从这个参数perm参数拿…

软件测试学习笔记丨curl命令发送请求

本文转自测试人社区,原文链接:https://ceshiren.com/t/topic/32332 一、简介 cURL是一个通过URL传输数据的,功能强大的命令行工具。cURL可以与Chrome Devtool工具配合使用,把浏览器发送的真实请求还原出来,附带认证信…

Spring Boot打造:小徐影院管理平台

第1章 概述 1.1 研究背景 随着现代网络技术发展,对于小徐影城管理系统现在正处于网络发展的阶段,所以对它的要求也是比较严格的,要从这个系统的功能和用户实际需求来进行对系统制定开发的发展方式,依靠网络技术的的快速发展和现代…

【Mysql】Mysql数据库基本操作-------DDL(上)

1、DDL解释 DDL(Data Definition Language),数据定义语言,该语言部分包括以下内容, (1)对数据库的常用操作 (2)对表结构的常用操作 (3)修…

【Qt】编写第一个Qt程序 对象树 内存泄漏问题探讨

编写第一个Qt程序 1. 使用图形化界面生成2. 使用代码生成3. 对象树3.1什么是对象树3.2 验证对象树 4. 解决编码问题 1. 使用图形化界面生成 创建好一个项目后,我们可以点击 widget.ui 进入图形化界面设计,可以直接通过拖拽的方式进行添加。 通过拖拽的方…

群体神经科学中的社会公正:避免数据分析中的社会构建误用

TLDR:有意思的观点。文章作者强调,使用群体神经科学大数据时,研究人员必须承担起伦理和科学责任,避免对边缘化群体造成进一步的污名化。通过像ABCD这样的开放数据集,研究人员应避免将种族、性别等简单化处理为独立变量…