JVM详解——类的加载

news2024/11/16 19:33:41

文章目录

  • 类的加载
    • 1、Java程序如何运行
    • 2、Java字节码文件
    • 3、类加载
    • 4、类加载的过程
    • 5、类加载器
    • 6、类的加载方式
    • 7、类的加载机制
    • 8、双亲委派机制
    • 9、破坏双亲委派机制

类的加载

在这里插入图片描述

1、Java程序如何运行

  • 首先通过Javac命令将.java文件编译生成.class字节码文件。
    Javac是Java编译命令,编译过程分为四步。

    1. 词法解析,通过空格分隔出单词、操作符、控制符等信息,形成信息流传递给语法解析器。
    2. 语法解析,将信息流按照Java语法规则组装成语法树。
    3. 语义分析,检查类型是否匹配、关键词是否使用合理、作用域是否正确等。
    4. 字节码生产,将经过1、2、3步骤生产的新型转换为字节码。
  • .class文件加载到JVM中经过一系列类加载流程,由解释器解释执行和JIT即时编译器将字节码文件编译成本地机器码执行。

    字节码必须通过类加载机制加载到JVM后方能执行,执行有三种模式,解释执行、JIT编译执行、JIT编译和解释器混合执行(主流JVM默认执行的方式)。混合模式优势在于解释器在启动时先解释执行,节省编译时间。
    解释执行: 来一行代码,解释一行,大部分不常用的代码,采用此种方式
    即时编译: 对于部分热点代码,虚拟机将该部分字节码编译生成机器指令,以提高Java虚拟机的运行效率

  • CPU调度线程执行本地机器码


2、Java字节码文件

Class文件本质上是一个以字节为基础单位的二进制流,各个数据项目严格按照顺序紧凑的排列在class文件中。JVM根据其特定的规则解析该二进制数据,从而得到相关信息。Class文件采用一种伪结构来存储数据,它有两种类型:无符号数和表。

Class文件的结构属性:

  1. 魔数与class文件的版本:class文件头4个字节称为魔数,是class文件的标识
  2. 常量池:class文件的资源仓库,存储变量的属性、类型和名称;方法的属性、类型和名称等。
  3. 访问标志:表示该class的属性和访问类型,比如class是类还是接口,访问类型是public、private,类型是否被标记为final
  4. 类索引、父类索引、接口索引:一种描述的数据项目,class文件凭此确定类的继承和实现关系
  5. 字段表属性:描述类或接口中声明的变量。比如变量的作用域(public、private、protected)、是否是静态变量(static)、可变性(final)、数据类型(基本数据类型、对象、数组)等
  6. 方法表属性:描述方法的类型、作用域、返回值、参数、是否是重写或重载
  7. 属性表属性:描述某些场景专有的信息。比如字段表中的特殊属性、方法表中的特殊属性。

3、类加载

Class 文件中描述的各类信息都需要加载到虚拟机后才能使用。JVM 把描述类的数据从 Class 文件加载到内存,并对数据进行加载、验证、解析和初始化,最终形成可以被虚拟机直接使用的 数据类型,这个过程称为虚拟机的类加载过程

与编译时需要连接的语言不同,Java 中类型的加载、连接和初始化都是在运行期间完成的,这增加了性能开销,但却提供了极高的扩展性,Java 动态扩展的语言特性就是依赖运行期动态加载和连接实现的。


4、类加载的过程

一个类从被加载到虚拟机内存开始,到卸载出内存为止,整个生命周期经历加载验证、准备、解析、初始化、使用和卸载七个阶段,其中验证、解析和初始化三个部分称为连接。加载、验证、准备、初始化阶段开始的先后顺序是确定的,解析则不一定:可能在初始化之后再开始,这是为了支持 Java 语言的动态绑定。
在这里插入图片描述

  1. 加载:查找并加载类的二进制数据
    在加载阶段,虚拟机需要完成以下三个步骤:

    • 通过一个类的全限定名来获取其定义的二进制字节流。
    • 将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
    • 在Java堆内存中生成一个代表这个类的java.lang.Class对象,作为对方法区中这些数据的访问入口。
  2. 验证: 确保被加载类的正确性
    确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。验证分为4个阶段:文件格式验证、元数据验证、字节码验证、符号引用验证。

验证阶段是非常重要的,但不是必须的,它对程序运行期没有影响,如果所引用的类经过反复验证,那么可以考虑采用-Xverifynone参数来关闭大部分的类验证措施,以缩短虚拟机类加载的时间。

  1. 准备:为类的静态变量分配内存,并将其初始化为默认值
    准备阶段是正式为类变量分配内存并设置类变量初始值零值的阶段,这些内存都将在方法区中分配。
    注: 此时分配的是类变量(static),不包括实例变量。初始化的值是数据类型的默认零值,比如0、0L、null、false 等。

  2. 解析:把类中的符号引用转换为直接引用
    解析阶段是虚拟机将常量池内的符号引用替换为直接引用的过程,解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄和调用点限定符7类符号引用进行。符号引用就是一组符号来描述目标,可以是任何字面量。

直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄

  1. 初始化:JVM对类进行初始化赋值
    为类的静态变量赋予正确的初始值,JVM负责对类进行初始化,主要对类变量进行初始化。

在Java中对类变量进行初始值设定有两种方式:

  • 声明类变量是指定初始值
  • 使用静态代码块为类变量指定初始值

JVM初始化步骤:

  • 假如这个类还没有被加载和连接,则程序先加载并连接该类
  • 假如该类的直接父类还没有被初始化,则先初始化其直接父类
  • 假如类中有初始化语句,则系统依次执行这些初始化语句
  1. 使用:使用类的对象实例
    类访问方法区内的数据结构的接口, 对象是堆区的数据。
  2. 卸载:类被卸载出内存
    Java虚拟机结束,类被卸载出内存。
    Java虚拟机结束生命周期的情况:
    • 执行了System.exit()方法
    • 程序正常执行结束
    • 程序在执行过程中遇到了异常或错误而异常终止
    • 由于操作系统出现错误而导致Java虚拟机进程终止

5、类加载器

启动类加载器:
Bootstrap ClassLoader,负责加载存放在JDK的安装目录下的jre\lib中,或被-Xbootclasspath参数指定的路径中的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载)。启动类加载器是无法被Java程序直接引用的。

扩展类加载器:

Extension ClassLoader,该加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载JDK安装目录下的jre\lib\ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。

应用程序类加载器:

Application ClassLoader,该类加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。


6、类的加载方式

类加载有三种方式:

1、命令行启动应用时候由JVM初始化加载

2、通过Class.forName()方法动态加载

3、通过ClassLoader.loadClass()方法动态加载

Class.forName()和ClassLoader.loadClass()区别?

  • Class.forName(): ​将类的.class文件加载到JVM中,还会对类进行解释,执行类中的static块
  • ClassLoader.loadClass(): ​只会将.class文件加载到JVM中,不会执行static中的内容,只有在newInstance() 方法创建类对象时才会去执行static块
  • Class.forName(name, initialize, loader)带参函数也可控制是否加载static块。并且只有调用了newInstance()方法构造函数创建类的对象时才会加载static块

7、类的加载机制

  1. 缓存机制,缓存机制将会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区寻找该Class,只有缓存区不存在,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓存区。(修改了class后,需要重启虚拟机,程序的修改才会生效)
  2. 双亲委派机制, 如果一个类加载器收到了类加载的请求,首先不会自己去尝试加载这个类,而是把请求委托给父加载器去完成,依次向上。因此,所有的类加载请求最终都应该被传递到顶层的启动类加载器中,只有当父加载器在它的搜索范围中没有找到所需的类时,也就是无法完成该加载,子加载器才会尝试自己去加载该类。

8、双亲委派机制

  1. AppClassLoader加载一个class时,它首先不会自己去尝试加载这个类,而是把类加载请求委派给父类加载器ExtClassLoader去完成。

  2. ExtClassLoader加载一个class时,它首先也不会自己去尝试加载这个类,而是把类加载请求委派给BootStrapClassLoader去完成。

  3. 如果BootStrapClassLoader加载失败(例如在$JAVA_HOME/jre/lib里未查找到该class),会使用ExtClassLoader来尝试加载;

  4. ExtClassLoader也加载失败,则会使用AppClassLoader来加载,如果AppClassLoader也加载失败,则会报出异常ClassNotFoundException

双亲委派机制的好处:

  • 系统类防止内存中出现多份同样的字节码(比如自己写的String类JDK中的String类会优先使用JDK中的系统API)
  • 保证Java程序安全稳定运行
  • 解决了各个类加载器的基础类统一问题

9、破坏双亲委派机制

为什么要破坏双亲委派?

举个🌰: 我们常用数据库驱动Driver接口,Driver定义在JDK中,但其实现是各个数据库服务商,比如:MySQLMYSQL CONNECROR,因此DriverManger要加载各个Driver接口实现类进行管理,但是DriverManager是由启动类加载器进行加载的,而这个启动类加载器默认值加载JDK安装目录下面的lib文件下的类库,但我们真正要加载的是各个实现类,需要有应用程序类加载器进行加载,这个时候就需要启动类加载器委托应用程序类加载器去加载Driver实现类,从而破坏了双亲委派。

破坏方式:

  1. 自定义类加载,重写loadclass方法。双亲委派的机制都是通过这个方法实现的,这个方法可以指定类通过什么类加载器来进行加载,所以如果改写他的加载规则,相当于打破双亲委派机制。
  2. 线程上下文类加载器: 提供父类加载器访问子类加载器的行为。
    双亲委派很好的解决了各个类加载器的基础类统一问题,基础类总是被用户代码所调用,但是如果基础类又要重新调用用户代码,此时就与双亲委派模型的设计理念相违背。
    比如:JNDI服务(JDBC/JCE/JAXB/JBI)是Java的标准服务,它的代码是由启动类加载器进行加载的,但是JNDI的作用就是进行资源的集中管理和查找,它需要调用由(服务厂商提供的实现类)开发人员在classpath下的类代码,但是启动类加载器不会进行加载。
    所以引入线程上下类加载器,通过java.lang.Thread类的setContextClassLoader()方法进行设置。如果创建线程时还未设置,它会从父线程继承一个,如果在应用程序全局范围内没有设置,那么这个线程上下类加载器就是应用程序类加载器。
  3. Java热部署
    Java热部署的规范化模块是OSGi提供的,热部署实现的关键就是OSGi自定义了类加载器,它为每个模块都配了一个类加载器。当需要动态地更换一个模块的时候,就把模块连通这个模块的类加载器一起替换,从而实现了热替换。此时类加载器从树状结构变为了网状结构,有大量的层与层之间的类加载器,所以就打破了双亲委派模型。

参考文章:

  • https://pdai.tech/md/java/jvm/java-jvm-classload.html
  • https://blog.csdn.net/Wangxichuan_Jack/article/details/123711799
  • https://blog.csdn.net/Fqzzzzz/article/details/123989751
  • https://blog.csdn.net/weixin_45629285/article/details/128050932

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

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

相关文章

【IOS逆向】frida-trace小计

【IOS逆向】frida-trace小计 当越狱完成之后,可以尝试品尝下frida frida-trace 用于跟踪函数或者 Objective-C 方法的调用,frida-trace -h 能够查看它的帮助,最重要的有下面几个参数: -i 跟踪某个函数,-x 排除某个函…

链表OJ(三) 反转链表合集

目录 反转链表 反转链表 II 链表中的节点每k个一组翻转 描述 给定一个单链表的头结点pHead(该头节点是有值的,比如在下图,它的val是1),长度为n,反转该链表后,返回新链表的表头。 数据范围: 0≤n≤10000≤…

开发必备的IDEA 插件!效率提升 50 倍!

日常开发中,面向百度编程的程序员,很多时候,你跟大佬级别的差距,可能不仅仅是知识面的差距,还有就是开发效率的差距。以下是我常用的几个IDEA插件,废话不多说,直接肝干货! 1. Codot…

庄懂的TA笔记(十二)<>

庄懂的TA笔记(十二)<>一、作业展示,答疑:1、作业:2、答疑:二、作业示范,分析:1、文档分析:2、资源分析:3、资源优化:4、光…

Linux下的进程控制

目录 退出码 终止进程 进程等待 进程程序替换 自己实现简易shell命令行 内建命令 退出码 在编写代码时main函数内部我们通常都使用return 0;结尾,以此标识正常退出。这里的return 0就是所谓的退出码,Linux下也是一样: 看这个小程序&…

【Opencv 系列】第1章 图像基础

通过本套课程,可以学到: 1.opencv的基本操作 2.两个案例,目标追踪&人脸识别 对重点内容,我会提示,包括我再准备这套课程过程中遇到的坑点! 最后代码我会放到git上,章节顺序一致:https://github.com/justinge/opencv_tutorial.git 系列文章目录 第1章 Opencv 图像基础 和 …

21基于二阶锥规划的主动配电网最优潮流求解

参考文献:主动配电网多源协同运行优化研究-乔珊主动配电网最优潮流研究及其应用实例-高红均主要内容:CPLEX二阶锥规划考虑WindCBSVGOLTCESS多时段24h,骨灰级注释,一看就懂!!!部分程序&#xff1…

基于注意力的知识蒸馏Attention Transfer原理与代码解析

paper:Paying More Attention to Attention: Improving the Performance of Convolutional Neural Networks via Attention Transfercode:https://github.com/megvii-research/mdistiller/blob/master/mdistiller/distillers/AT.py背景一个流行的假设是存…

SpringCloudAlibaba-Sentinel

一、介绍官网&#xff1a;https://github.com/alibaba/Sentinel/下载jar包,启动,访问http://localhost:8080/创建module添加如下依赖<!--SpringCloud ailibaba sentinel --><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring…

内网渗透(四十)之横向移动篇-ms14-068传递获取域管横向移动

系列文章第一章节之基础知识篇 内网渗透(一)之基础知识-内网渗透介绍和概述 内网渗透(二)之基础知识-工作组介绍 内网渗透(三)之基础知识-域环境的介绍和优点 内网渗透(四)之基础知识-搭建域环境 内网渗透(五)之基础知识-Active Directory活动目录介绍和使用 内网渗透(六)之基…

[软件工程导论(第六版)]第2章 可行性研究(复习笔记)

文章目录2.1 可行性研究的任务2.2 可行性研究过程2.3 系统流程图2.4 数据流图概念2.5 数据字典2.6 成本/效益分析2.1 可行性研究的任务 可行性研究的目的 用最小的代价在尽可能短的时间内确定问题是否能够解决。 可行性研究的3个方面 &#xff08;1&#xff09;技术可行性&…

宝塔搭建实战人才求职管理系统adminm前端vue源码(三)

大家好啊&#xff0c;我是测评君&#xff0c;欢迎来到web测评。 上一期给大家分享骑士cms后台admin前端vue在本地运行打包、宝塔发布部署的方式&#xff0c;本期给大家分享&#xff0c;后台adminm移动端后台vue前端怎么在本地运行&#xff0c;打包&#xff0c;实现线上功能更新…

Ubuntu下使用Wine运行HBuilderX

安装完wine后&#xff0c;在HbuilderX的目录中打开终端&#xff0c;直接输入wine HBuilderX.exe命令&#xff0c;启动过程中会提示安装wine-mono组件&#xff0c;点击安装按钮下载安装该组件&#xff0c;该组件下载速度慢&#xff0c;需要等待特别长时间。   安装完毕后&…

金三银四软件测试工程师面试题(含答案)

前言&#xff1a;此文专门记载本人平时面试以及收藏的面试题目&#xff0c;如果有错误之处请及时指正&#xff0c;谢谢&#xff01; 1、python的数据类型有哪些 答&#xff1a;Python基本数据类型一般分为&#xff1a;数字、字符串、列表、元组、字典、集合这六种基本数据类…

pytorch配置—什么是CUDA,什么是CUDNN、在配置pytorch虚拟环境中遇到的问题、在安装gpu—pytorch中遇到的问题

1.什么是CUDA&#xff0c;什么是CUDNN &#xff08;1&#xff09;什么是CUDA CUDA(ComputeUnified Device Architecture)&#xff0c;是显卡厂商NVIDIA推出的运算平台。 CUDA是一种由NVIDIA推出的通用并行计算架构&#xff0c;该架构使GPU能够解决复杂的计算问题。 &#xff0…

RuoYi-Vue-Plus搭建(若依)

项目简介 1.RuoYi-Vue-Plus 是重写 RuoYi-Vue 针对 分布式集群 场景全方位升级(不兼容原框架)2.环境安装参考&#xff1a;https://blog.csdn.net/tongxin_tongmeng/article/details/128167926 JDK 11、MySQL 8、Redis 6.X、Maven 3.8.X、Nodejs > 12、Npm 8.X3.IDEA环境配置…

建造《流浪地球2》中要毁灭人类的超级量子计算机MOSS的核心量子技术是什么?

1.《流浪地球2》中的量子计算机 2023年中国最火的电影非《流浪地球2》莫属&#xff0c;在《流浪地球2》中有一个人工智能机器人MOSS &#xff0c;它的前身是“550W”超级量子计算机&#xff0c;“MOSS”是它给自己起的名字&#xff08;“550W”倒转180度就是“MOSS”&#xff…

力扣38.外观数列

文章目录力扣38.外观数列题目描述方法1&#xff1a;按规则生成&#xff08;顺序暴力法&#xff09;力扣38.外观数列 题目描述 给定一个正整数 n &#xff0c;输出外观数列的第 n 项。 「外观数列」是一个整数序列&#xff0c;从数字 1 开始&#xff0c;序列中的每一项都是对…

这才是计算机科学_人工智能

人工智能一、前言二、ML2.1 分类2.1.1 决策树2.2.2 支持向量机2.2.3 人工神经网络三、计算机视觉3.1 Prewitt算子3.2 Viola-Jones 人脸检测算法3.3 卷积神经网络四、自然语言处理4.1 知识图谱4.2 语音识别一、前言 之前讲了计算机从发展到现在的过程&#xff0c;计算机很适合做…

[软件工程导论(第六版)]第2章 可行性研究(课后习题详解)

文章目录1. 在软件开发的早期阶段为什么要进行可行性研究&#xff1f;应该从哪些方面研究目标系统的可行性&#xff1f;2. 为方便储户&#xff0c;某银行拟开发计算机储蓄系统。储户填写的存款单或取款单由业务员输入系统&#xff0c;如果是存款&#xff0c;系统记录存款人姓名…