JVM—类加载子系统

news2024/11/26 8:49:10

JVM细节版架构图

本文针对Class Loader SubSystem这一块展开讲解类加载子系统的工作流程

类加载子系统作用

1.类加载子系统负责从文件系统或者网络中加载class文件,class文件在文件开头有特定的文件标识即16进制CA FE BA BE;

2.加载后的Class类信息存放于一块成为方法区的内存空间。除了类信息之外,方法区还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)

来一张经典的JVM内存结构图:其中类加载器的工作范围只限于下图的左半部分,不包含调用构造器实例化对象

3.ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定

4.如果调用构造器实例化对象,则其实例存放在堆区

类加载子系统功能细分

加载模块

1.通过一个类的全限定名获取定义此类的二进制字节流;

2.将这个字节流所代表的的静态存储结构转化为方法区的运行时数据;

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

链接模块分为三块,即验证、准备、解析

验证

1.目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全。

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

准备

1.为类变量分配内存并且设置该类变量的默认初始值,即零值;

2.这里不包含用final修饰的sttic,因为final在编译的时候就会分配了,准备阶段会显式初始化;

3.之类不会为实例变量分配初始化,类变量会分配在方法去中,而实例变量是会随着对象一起分配到java堆中。

解析

1.将常量池内的符号引用转换为直接引用的过程。

2.事实上,解析操作网晚会伴随着jvm在执行完初始化之后再执行

3.符号引用就是一组符号来描述所引用的目标。符号应用的字面量形式明确定义在《java虚拟机规范》的class文件格式中。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄

4.解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT_Class_info/CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。

初始化模块,初始化阶段就是执行类构造器方法clinit()的过程

1.clinit()即“class or interface initialization method”,注意他并不是指构造器init()

2.此方法不需要定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。 

3.我们注意到如果没有静态变量c,那么字节码文件中就不会有clinit方法

构造器方法clinit()中指令按语句在源文件中出现的顺序执行

虚拟机必须保证一个类的clinit()方法在多线程下被同步加锁

即一个类只需被clinit一次,之后该类的内部信息就被存储在方法区。

 可以看到线程2并不会重复执行初始化操作

 类加载器分类

1.JVM支持两种类型的加载器,分别为引导类加载器C/C++实现(BootStrap ClassLoader)自定义类加载器由Java实现

2.从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器

3.注意上图中的加载器划分关系为包含关系,并不是继承关系

4.按照这样的加载器的类型划分,在程序中我们最常见的类加载器是:引导类加载器BootStrapClassLoader、自定义类加载器(Extension Class Loader、System Class Loader、User-Defined ClassLoader)

自定义类与核心类库的加载器

1.对于用户自定义类来说:将使用系统类System Class Loader加载器中的AppClassLoader进行加载

2.java核心类库都是使用引导类加载器BootStrapClassLoader加载的

/**
 * ClassLoader加载
 */
public class ClassLoaderTest {
    public static void main(String[] args) {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //获取其上层  扩展类加载器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);//sun.misc.Launcher$ExtClassLoader@610455d6

        //获取其上层 获取不到引导类加载器
        ClassLoader bootStrapClassLoader = extClassLoader.getParent();
        System.out.println(bootStrapClassLoader);//null

        //对于用户自定义类来说:使用系统类加载器进行加载
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2

        //String 类使用引导类加载器进行加载的  -->java核心类库都是使用引导类加载器加载的
        ClassLoader classLoader1 = String.class.getClassLoader();
        System.out.println(classLoader1);//null获取不到间接证明了String 类使用引导类加载器进行加载的

    }
}

虚拟机自带的加载器

启动类加载器(引导类加载器,BootStrap ClassLoader)

1.这个类加载使用C/C++语言实现的,嵌套在JVM内部

2.它用来加载java的核心库(JAVA_HOME/jre/lib/rt.jar/resources.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类

3.并不继承自java.lang.ClassLoader,没有父加载器

4.加载拓展类和应用程序类加载器,并指定为他们的父加载器,即ClassLoader

5.出于安全考虑,BootStrap启动类加载器只加载包名为java、javax、sun等开头的类

拓展类加载器(Extension ClassLoader)

1.java语言编写 ,由sun.misc.Launcher$ExtClassLoader实现。

2.派生于ClassLoader类

3.从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会由拓展类加载器自动加载

应用程序类加载器(系统类加载器,AppClassLoader)

1.java语言编写, 由sun.misc.Launcher$AppClassLoader实现。

2.派生于ClassLoader类

3.它负责加载环境变量classpath或系统属性 java.class.path指定路径下的类库

4.该类加载器是程序中默认的类加载器,一般来说,java应用的类都是由它来完成加载

5.通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器

代码演示

/**
 * 虚拟机自带加载器
 */
public class ClassLoaderTest1 {
    public static void main(String[] args) {
        System.out.println("********启动类加载器*********");
        URL[] urls = sun.misc.Launcher.getBootstrapClassPath().getURLs();
        //获取BootStrapClassLoader能够加载的api路径
        for (URL e:urls){
            System.out.println(e.toExternalForm());
        }

        //从上面的路径中随意选择一个类 看看他的类加载器是什么
        //Provider位于 /jdk1.8.0_171.jdk/Contents/Home/jre/lib/jsse.jar 下,引导类加载器加载它
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println(classLoader);//null

        System.out.println("********拓展类加载器********");
        String extDirs = System.getProperty("java.ext.dirs");
        for (String path : extDirs.split(";")){
            System.out.println(path);
        }
        //从上面的路径中随意选择一个类 看看他的类加载器是什么:拓展类加载器
        ClassLoader classLoader1 = CurveDB.class.getClassLoader();
        System.out.println(classLoader1);//sun.misc.Launcher$ExtClassLoader@4dc63996
    }
}

知识扩展:启动类加载器BootStrapClassLoader能够加载的api路径有

最近看java.util.concurrent包的内容,发现java.time.、java.util.、java.nio.、java.lang.、java.text.、java.sql.、java.math.*等等都在rt.jar包下,

为什么要使用用户自定义类加载器

1.隔离加载类

2.修改类加载的方式

3.拓展加载源

4.防止源码泄漏

ClassLoader的常用方法及获取方法

ClassLoader类,它是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动类加载器)

ClassLoader继承关系

 代码示例如下

双亲委派机制

Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将她的class文件加载到内存生成的class对象。而且加载某个类的class文件时,java虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派 模式

双亲委派机制工作原理

代码示例

如图,虽然我们自定义了一个java.lang包下的String尝试覆盖核心类库中的String,但是由于双亲委派机制,启动加载器会加载java核心类库的String类(BootStrap启动类加载器只加载包名为java、javax、sun等开头的类),而核心类库中的String并没有main方法

双亲委派机制的优势

1.避免类的重复加载,如上

2.保护程序安全,防止核心API被随意修改

启动类加载器可以抢在标准扩展类装载器之前去装载类,而标准扩展类装载器可以抢在类路径加载器之前去装载那个类,类路径装载 器又可以抢在自定义类加载器之前去加载它。所以Java虚拟机先从最可信的Java核心API查找类型,这是为了防止不可靠的类扮演被信任的类,试想一 下,网络上有个名叫java.lang.Integer的类,它是某个黑客为了想混进java.lang包所起的名字,实际上里面含有恶意代码,但是这种 伎俩在双亲模式加载体系结构下是行不通的,因为网络类加载器在加载它的时候,它首先调用双亲类加载器,这样一直向上委托,直到启动类加载器,而启动类加载 器在核心Java API里发现了这个名字的类,所以它就直接加载Java核心API的java.lang.Integer类,然后将这个类返回,所以自始自终网络上的 java.lang.Integer的类是不会被加载的。

3.保证核心API包的访问权限

但是如果这个移动代码不是去试图替换一个被信任的类(就是前面说的那种情况),而是想在一个被信任的包中插入一个全新的类型,情况会怎样呢?比如一个名为 java.lang.Virus的类,经过双亲委托模式,最终类装载器试图从网络上下载这个类,因为网络类装载器的双亲们都没有这个类(当然没有了,因为 是病毒嘛)。假设成功下载了这个类,那你肯定会想,Virus和lang下的其他类痛在java.lang包下,暗示这个类是Java API的一部分,那么是不是也拥有修改Java.lang包中数据的权限呢?答案当然不是,因为要取得访问和修改java.lang包中的权 限,java.lang.Virus和java.lang下其他类必须是属于同一个运行时包的,什么是运行时包?运行时包是指由同一个类装载器装载的、属 于同一个包的、多个类型的集合。考虑一下,java.lang.Virus和java.lang其他类是同一个类装载器装载的吗?不是 的!java.lang.Virus是由网络类装载器装载的!

自定义类:java.lang.MeDsh(java.lang包需要访问权限,阻止我们用包名自定义类)

双亲委派机制在SPI中的应用#

1.某个应用程序由双亲委派机制找到引导类加载器,首先调用rt.jar包中的SPI核心,但由于SPI核心当中有各种各样的接口需要被实现(这里指具体的服务提供商),这里我们已JDBC.jar为例,jdbc.jar可以为我们提供具体的实现。

2.那么这时我们需要反向委托,找到线程上下文类加载器去加载jdbc.jar

3.线程上下文类加载器属于系统类加载器

JVM中表示两个class对象是否为同一个类

1.在jvm中表示两个class对象是否为同一个类存在的两个必要条件

类的完整类名必须一致,包括包名

即使类的完整类名一致,同时要求加载这个类的ClassLoader(指ClassLoader实例对象)必须相同;是引导类加载器、还是定义类加载器

2.换句话说,在jvm中,即使这两个类对象(class对象)来源同一个Class文件,被同一个虚拟机所加载,但只要加载它们的ClassLoader实例对象不同,那么这两个类对象也是不相等的.

3.对类加载器的引用,JVM必须知道一个类型是有启动类加载器加载的还是由用户类加载器加载的。如果一个类型由用户类加载器加载的,那么jvm会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。当解析一个类型到另一个类型的引用的时候,JVM需要保证两个类型的加载器是相同的。

类的主动使用和被动使用

java程序对类的使用方式分为:主动使用和被动使用,即是否调用了clinit()方法

主动使用在类加载系统中的第三阶段initialization即初始化阶段调用了clinit()方法

而被动使用不会去调用

主动使用,分为七种情况

1.创建类的实例

2.访问某各类或接口的静态变量,或者对静态变量赋值

3.调用类的静态方法

4.反射 比如Class.forName(com.dsh.jvm.xxx)

5.初始化一个类的子类

6.java虚拟机启动时被标明为启动类的类

7.JDK 7 开始提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF_putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化
 

除了以上七种情况,其他使用java类的方式都被看作是对类的被动使用,都不会导致类的初始化。

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

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

相关文章

Flink SQL Checkpoint 学习总结

前言 学习总结Flink SQL Checkpoint的使用,主要目的是为了验证Flink SQL流式任务挂掉后,重启时还可以继续从上次的运行状态恢复。 验证方式 Flink SQL流式增量读取Hudi表然后sink MySQL表,任务启动后处于running状态,先查看sin…

IDEA 断点总是进入class文件没有进入源文件解决

前言 idea 断点总是进入class文件没有进入源文件解决 问题 在源文件里打了断点,断点模式启动时却进入了class文件里的断点,而没有进入到java源文件里的断点。 比如:我在 A.java 里打了断点,调试时却进入到了 jar 包里的 A.clas…

基于ensp的小型局域网网络搭建及需求分析

一 需求分析本实验的目的在于建立小型局域网。由于公司由财政部、人事部、科技部三个部门组成,分布在同一个交换机下。设计以下网络:三个个部门使用两台交换机连接,然后连接到汇聚交换机,再通过路由器与外网以及其他部门网络相连。…

java TCP Socket 数据传输,服务端与客户端

java TCP Socket 数据传输,服务端与客户端 1. socket通信基本原理 socket 通信是基于TCP/IP 网络层上的一种传送方式,我们通常把TCP和UDP称为传输层。 如上图,在七个层级关系中,我们讲的socket属于传输层,其中UDP是…

单位冲激函数与单位阶跃函数

目录 单位阶跃函数 单位冲激函数 狄拉克定义: 冲激函数的性质 对时间的积分等于单位阶跃函数 筛分性质 尺度特性 冲击偶的定义:单位冲击函数的导数 各阶导数 离散的阶跃信号与冲激信号 (1)阶跃信号,其定义为…

vi编辑器操作指令分享

vi编辑器是所有Unix及Linux系统下标准的编辑器,它的强大不逊色于任何最新的文本编辑器,这里只是简单地介绍一下它的用法和一小部分指令。由于对Unix及Linux系统的任何版本,vi编辑器是完全相同的,因此您可以在其他任何介绍vi的地方…

国内vs国外:外贸建站该如何选择?

外贸建站找国内还是国外? 答案是:国内。 随着互联网的发展,越来越多的企业开始意识到在网络上进行商业活动的重要性。 其中,建立一个专业的外贸网站是企业在国际市场上拓展业务的关键。 然而,对于选择国内还是国外…

电脑出问题了怎么重装系统修好

电脑在使用过程中经常会出现各种各样的问题,如系统崩溃、蓝屏、病毒感染等。这些问题如果不能及时得到解决,将会给用户带来很多麻烦和损失。小白一键重装系统是一个功能强大的工具,可以帮助用户快速解决电脑常见问题。下面我们就来详细介绍如…

Vulnhub靶场----8、DC-8

文章目录一、环境搭建二、渗透流程三、思路总结一、环境搭建 DC-8下载地址:https://download.vulnhub.com/dc/DC-8.zip kali:192.168.144.148 DC-8:192.168.144.156 二、渗透流程 1、信息收集nmap -T5 -A -p- -sV -sT 192.168.144.156思路&am…

每日分享(四合一即时通讯聊天源码APP群聊、私聊、朋友圈)

demo软件园每日更新资源,请看到最后就能获取你想要的: 1.Python整洁编程 完整版PDF Python 与其他语言的不同之处在于,它是一种简单而有深度的语言。因为简单,所以谨慎编写代码要重要得多,尤其是在大项目中,因为代码很容易变得复…

ros2创建一个工程

第一步:创建src目录 $ mkdir ros2-demo $ cd ros2-demo/ $ mkdir src $ cd src/第二步:创建功能包cd src$ ros2 pkg create --build-type ament_cmake ros2_demo --dependencies rclcpp std_msgsros2 pkg create --build-type ament_python learning_pkg…

打地鼠游戏-第14届蓝桥杯STEMA测评Scratch真题精选

[导读]:超平老师的《Scratch蓝桥杯真题解析100讲》已经全部完成,后续会不定期解读蓝桥杯真题,这是Scratch蓝桥杯真题解析第102讲。 蓝桥杯选拔赛现已更名为STEMA,即STEM 能力测试,是蓝桥杯大赛组委会与美国普林斯顿多…

外贸建站多少钱?不同预算对应的建站方案!

外贸建站多少钱? 答案是:3000左右。 作为一个外贸企业的经营者,我们深知一个优质的外贸网站对于企业的重要性。 然而,建立一个优质的外贸网站需要耗费大量的时间和资金,因此我们需要在预算有限的情况下,…

代码随想录算法训练营第三天| 203. 移除链表、707. 设计链表、206.反转链表

977 有序数组的平方题目链接:203 移除链表元素介绍给你一个链表的头节点 head 和一个整数 val ,请你删除链表中所有满足 Node.val val 的节点,并返回 新的头节点 。思路一如果要被删除的元素是非头节点,只需要找到前一个节点&…

MySQL之幻读问题

MySQL之幻读问题 导读 在进入今天的主题之前必须先了解事务的四大特性ACID、MVCC、事务隔离级别(具体的自行查询),其中I(Isolation)隔离性所产生的问题涉及到的事务隔离分为不同级别,包括读未提交&#x…

python+pytest接口自动化(7)-cookie绕过登录(保持登录状态)

在编写接口自动化测试用例或其他脚本的过程中,经常会遇到需要绕过用户名/密码或验证码登录,去请求接口的情况,一是因为有时验证码会比较复杂,比如有些图形验证码,难以通过接口的方式去处理;再者&#xff0c…

Java多线程——线程安全、synchronized、volatile关键字以及多线程案例

文章目录前言一、线程安全—多线程不可避免的风险!1、线程不安全的示例2、线程不安全的原因二、synchronized关键字1.synchronized的特性1)互斥2)刷新内存3)可重入2、synchronized使用示例3、Java标准库中的线程安全类三、volatil…

【巨人的肩膀】JAVA面试总结(四)

💪、JVM 目录💪、JVM1、说一下JVM的主要组成部分及其作用2、什么是JVM内存结构(谈谈对运行时数据区的理解)3、堆和栈的区别是什么4、堆中存什么?栈中存什么?5、为什么不把基本类型放堆中呢?6、为…

理论上BI软件适配任何行业,但为什么有些行业做不了?

BI商业智能是一种通用的数据类技术解决方案。理论上来说,BI商业智能的核心是数据。只要企业有数据累积,就可以在BI软件上展开一系列的数据开发,获取决策所需的数据依据。但在现实中,却会发现有些BI软件对特定的行业束手无策&#…

【站外SEO】如何利用外部链接来提高你的网站排名

随着互联网的快速发展,越来越多的企业开始注重SEO优化,以提升自己的网站排名,增加流量和曝光度。 而站外SEO作为SEO的重要组成部分,对于提升网站排名具有不可忽视的作用。 站外SEO主要是通过外部链接来提高网站的排名。而GPB外链…