26-38-javajvm-类加载器子系统

news2024/9/23 5:32:27

26-javajvm-类加载器子系统:

1.内存结构概述

在这里插入图片描述

在这里插入图片描述

2.类加载子系统

2.1 类加载器子系统的作用
1)、类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识。
2)、ClassLoader只负责class文件的加载,至于它是否可以运行,则由Execution Engine决定。
3)、加载的类信息存放于一块称为方法区的内存空间。除了类的信息外,方法区中还会存放运行时常量池信息,可能还包括字符串字面量和数字常量(这部分常量信息是Class文件中常量池部分的内存映射)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JopFwv4v-1669901196988)(png/image-20211021163800188.png)]

2.2 类加载器ClassLoader角色

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZHTggDbC-1669901196989)(png/image-20211021164408091.png)]

​ 1)、class file存在于本地硬盘上,可理解为一个模板,而最终这个模板在执行的时候是要加载到JVM当中来根据这个文件实例化出n个一模一样的实例。
​ 2)、class file加载到JVM中,被称为DNA元数据模板,放在方法区。在.class文件 –> JVM –> 最终成为元数据模板,此过程就要一个运输工具(类装载器Class Loader),扮演一个快递员的角色

2.3 类的加载过程


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6qaQLyh8-1669901196990)(png/image-20211021164805548.png)]

public class HelloLoader {
    public static void main(String[] args) {
        System.out.println("ClassLoader加载....");
    }
}
执行 main( ) 方法(静态方法)就需要先加载承载类 HelloLoader
加载成功,则进行链接、初始化等操作,完成后调用 HelloLoader 类中的静态方法 main
加载失败则抛出异常
  • 完整流程:加载 --> 链接(验证 --> 准备 --> 解析) --> 初始化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BzP9c1Mi-1669901196990)(png/image-20211021165034891.png)]

2.3.1 加载(Loading)阶段

加载

​ 1)、通过一个类的全限定名获取定义此类的二进制字节流
​ 2)、将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构
​ 3)、在内存中生成一个代表这个类的java.lang.Class对象,作为方法区这个类的各种数据的访问入口

加载.class文件的方式

​ 1)、从本地系统中直接加载
​ 2)、通过网络获取,典型场景:Web Applet
​ 3)、从zip压缩包中读取,成为日后jar、war格式的基础
​ 4)、运行时计算生成,使用最多的是:动态代理技术
​ 5)、由其他文件生成,典型场景:JSP应用从专有数据库中提取.class文件,比较少见
​ 6)、从加密文件中获取,典型的防Class文件被反编译的保护措施

2.3.2 链接(Linking)阶段:链接分为三个子阶段:验证 --> 准备 --> 解析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L1cNQX6m-1669901196991)(png/image-20211025165347154.png)]

验证(Verify):

1)、目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全
2)、主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证。
3)、使用 BinaryViewer 查看字节码文件,其开头均为 CAFE BABE ,如果出现不合法的字节码文件,那么将会验证不通过

准备(Prepare):

  • 为类变量分配内存并且设置该类变量的默认初始值,即零值
  • 这里不包含用final修饰的static,因为final在编译的时候就会分配好了默认值,准备阶段会显式初始化
  • 注意:这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中
public class HelloApp {
    private static int a=1; //prepare:a=0准备阶段是基本数据的默认值,只有到初始化才会福实际的值。
    public static void main(String[] args) {
        System.out.println(a);
    }
}

解析(Resolve):

1、将常量池内的符号引用转换为直接引用的过程
2、事实上,解析操作往往会伴随着JVM在执行完初始化之后再执行
3、符号引用就是一组符号来描述所引用的目标。符号引用的字面量形式明确定义在《java虚拟机规范》的class文件格式中。直接引用就是直接指向目标的指针、相对偏移量或一个间接定位到目标的句柄
4、解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT Class info、CONSTANT Fieldref info、CONSTANT Methodref info等

反编译后的引用
   #1 = Methodref          #6.#23         // java/lang/Object."<init>":()V
   #2 = Fieldref           #24.#25        // java/lang/System.out:Ljava/io/PrintStream;
   #3 = Fieldref           #5.#26         // com/jvm/test/chapter02/HelloApp.a:I
   #4 = Methodref          #27.#28        // java/io/PrintStream.println:(I)V
   #5 = Class              #29            // com/jvm/test/chapter02/HelloApp
   #6 = Class              #30            // java/lang/Object

2.3.3 初始化(Initialization)阶段(IDEA安装 jclasslib Bytecode Viewer插件)
1、初始化阶段就是执行类构造器方法()的过程
2、此方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来。也就是说,当我们代码中包含static变量的时候,就会有( )方法;如果当前类不存在static变量,那么它的字节码文件是不会存在( )
3、()方法中的指令按语句在源文件中出现的顺序执行
4、()不同于类的构造器。(关联:构造器是虚拟机视角下的())
5、若该类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕
6、虚拟机必须保证一个类的()方法在多线程下被同步加锁

当我们代码中包含static变量的时候,就会有clinit方法

如果当前类不存在static变量,那么它的字节码文件是不会存在<clinit>( )

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

public class ClassInitTest {
    private static int num=1;
	//private int num=1;
    static {
        num=2;
        number=20;
        System.out.println(num);
//        System.out.println(number);//非法的前向引用,报错,声明在后面
    }
    //linking当中prepare准备环境为0-->initial:按照顺序覆盖,先是20,在是10
    private static int number=10;
    public static void main(String[] args) {
        System.out.println(ClassInitTest.num);
        System.out.println(ClassInitTest.number);    
    }
}

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SoDzvdsj-1669901196992)(png/image-20211025172241398.png)]

构造器是虚拟机视角下的<init>()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BckOOr3P-1669901196993)(png/image-20211025172906652.png)]

若该类具有父类,JVM会保证子类的<clinit>()执行前,父类的<clinit>()已经执行完毕

public class ClinitTest1 {
    static class Father {
        public static int A = 1;
        static {
            A = 2;
        }
    }
    static class Son extends Father {
        public static int B = A;
    }

    public static void main(String[] args) {
        //加载Father类,其次加载Son类。
        System.out.println(Son.B); //结果为2
    }
}
加载流程如下:
首先,执行 main( ) 方法需要加载 ClinitTest1 类
获取 Son.B 静态变量,需要加载 Son 类
Son 类的父类是 Father 类,所以需要先执行 Father 类的加载,再执行 Son 类的加载

虚拟机必须保证一个类的<clinit>()方法在多线程下被同步加锁,验证,当线程1未释放是,线程2无法获取该类的clinit方法

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SYKcmrcm-1669901196993)(png/image-20211025173330292.png)]

类加载器的分类

JVM支持两种类加载器 。分别为引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)
从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lFniR7oz-1669901196994)(png/image-20211115000043072.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VXIWPD6a-1669901196994)(png/image-20211115000105158.png)]

1.启动类加载器( 引导类加载器,Bootstrap ClassLoader )

1)这个类加载使用C/C++语言实现的,嵌套在JVM内部
2)用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jarresources.jar 或 sun.boot.class.path 路径下的内容),用于提供JVM自身需要的类并不继承自java.lang.ClassLoader,没有父加载器
3)加载扩展类和应用程序类加载器,并作为他们的父类加载器
4)出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类

2.扩展类加载器(Extension ClassLoader)

1)Java编写,由sun.misc.Launcher$ExtClassLoader实现,派生于ClassLoader类
2)从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的 jre / lib / ext子目录(扩展目录)下加载类库。若用户创建的 JAR 放在此目录下,也会自动由扩展类加载器加载

3。应用程序类加载器(系统类加载器,AppClassLoader) com.jvm.test.chapter02.classLoader.ClassLoaderTest

1)、Java编写,由sun.misc.LaunchersAppClassLoader实现,派生于ClassLoader类,父类加载器为扩展类加载器
2)、负责加载环境变量 classpath 或 系统属性java.class.path指定路径下的类库该类加载是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载的通过classLoader.getSystemclassLoader( )方法可以获取到该类加载器

在这里插入图片描述

用户自定义类加载器
1、隔离加载类 2、修改类加载的方式 3、扩展加载源 4、防止源码泄露

可通过继承抽象类java.lang.ClassLoader类,实现自己的类加载器,以满足一些特殊的需求。建议把自定义的类加载逻辑写在findclass( )方法中,若无太过复杂的需求,可直接继承URIClassLoader类,这样可以避免自己去编写findclass( )方法及其获取字节码流的方式,使自定义类加载器编写更加简洁。com.jvm.test.chapter02.classLoader.CustomClassLoader

双亲委派机制原理
Java虚拟机对 class 文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的 class 文件加载到内存中生成 class 对象。而且加载某个类的class文件时,Java虚拟机采用的是双亲委派模式,即把请求交由父类处理,它是一种任务委派模式如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行;如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。父类加载器一层一层往下分配任务,如果子类加载器能加载,则加载此类,如果将加载任务分配至系统类加载器也无法加载此类,则抛出异常

双亲委派机制的优势

  • 避免类的重复加载
  • 保护程序安全,防止核心API被随意篡改
    • 自定义类:java.lang.String 没有调用
    • 自定义类:java.lang.ShkStart(报错:阻止创建 java.lang开头的类)

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-amDFz5DB-1669901196996)(png/image-20211115224810046.png)]

沙箱安全机制: String引导类加载器,自定义的无法加载,箱子内部。外部无法干扰

如何判断两个class对象是否相同?1.完整类型相同 2、加载这个类的 ClassLoader类加载器要一样

类的主动使用和被动使用

Java程序对类的使用方式分为:主动使用 和 被动使用。

主动使用,又分为七种情况:

1)、创建类的实例
2)、访问某个类或接口的静态变量,或者对该静态变量赋值
3)、调用类的静态方法
4)、反射( 如:Class.forName(“zh.class.Test”) )
5)、初始化一个类的子类
6)、Java虚拟机启动时被标明为启动类的类
7)、JDK7开始提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化

除了以上七种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化,即不会执行初始化阶段(不会调用 clinit( ) 方法和 init( ) 方法)

)、反射( 如:Class.forName(“zh.class.Test”) )
5)、初始化一个类的子类
6)、Java虚拟机启动时被标明为启动类的类
7)、JDK7开始提供的动态语言支持:java.lang.invoke.MethodHandle实例的解析结果REF_getStatic、REF putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化

除了以上七种情况,其他使用Java类的方式都被看作是对类的被动使用,都不会导致类的初始化,即不会执行初始化阶段(不会调用 clinit( ) 方法和 init( ) 方法)

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

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

相关文章

常用的shell命令

常用的shell命令 1、ls命令 功能&#xff1a;显示文件和目录的信息 ls 以默认方式显示当前目录文件列表 ls -a 显示所有文件包括隐藏文件 ls -l 显示文件属性&#xff0c;包括大小&#xff0c;日期&#xff0c;符号连接&#xff0c;是否可读写及是否可执行 ls -lh 显示文件的…

1547_AURIX_TC275_CPU子系统_数据存储接口

全部学习汇总&#xff1a; GreyZhang/g_TC275: happy hacking for TC275! (github.com) 这个可以对比PMI来看一下&#xff0c;相比于PMI&#xff0c;DMI的结构简单很多。 1. 之前在看cache以及其对指令读取速度影响的时候&#xff0c;可能弄错了一个概念。如果cache不命中&…

【JavaEE】认识多线程(一)

✨哈喽&#xff0c;进来的小伙伴们&#xff0c;你们好耶&#xff01;✨ &#x1f6f0;️&#x1f6f0;️系列专栏:【JavaEE】 ✈️✈️本篇内容:了解多线程(初阶) &#x1f680;&#x1f680;代码存放仓库gitee&#xff1a;JavaEE初阶代码存放&#xff01; ⛵⛵作者简介&#x…

单变量微积分重点(2)

泰勒公式 用柯西定理证明 拉格朗日余项 麦克劳林展开式&#xff1a; 皮亚诺余项的泰勒公式&#xff1a; 弧长的微分 注意s(t)需要在后面证明&#xff08;定积分的知识&#xff09; 不定积分&#xff1a; 注意&#xff0c;不同的积分方法经常会得到不同的结果&#xff0c;但它们…

IDEA如何配置 Gradle 及 Gradle 安装过程(详细版)

IDEA如何配置 Gradle&#xff08;详细版&#xff09; 一、安装 Gradle 1、下载 Gradle 安装包 官网下载链接&#xff1a;https://gradle.org/releases/ 2、下载后解压 3、文件夹如图所示 二、环境变量配置 1、点击我的电脑->属性->高级系统设置->环境变量 2、新建&…

[附源码]JAVA毕业设计抗击新冠疫情专题宣传网站(系统+LW)

[附源码]JAVA毕业设计抗击新冠疫情专题宣传网站&#xff08;系统LW&#xff09; 目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 …

JSP+MySQL绿色环境保护网站的设计于实现

环保已经是当前中国的一个基本国策,国家领导人和各地政府也制定了一系列相关的政策来号召全民积极的参加到环保事业中来,为了能够更好的响应多家的号召我们开发了本JSP&#xff1a;MySQL&#xff1a;SSH 绿色环保网站,希望更多的人能够积极的参加到环保事业中来,本网站通过环境…

华为模拟器手把手安装教程-HCIE(华为网络工程师)

一、准备工作 请各位提前准备好eNSP_Setup安装程序、VirtualBox安装程序、Wireshark-win64位安装程序和WinPcap安装程序&#xff08;在wireshark安装过程中可以一起安装&#xff0c;也可以单独安装&#xff09;&#xff0c;获取相关安装程序文件可以联系小编哦&#xff01; 二、…

【蓝桥杯】第十四届模拟赛第一期及第二期填空汇总

目录 1.A题&#xff08;进制位数&#xff09; 位运算符 第一期 问题描述 解析 第二期 解析 代码 2.B题&#xff08;日期问题&#xff09; 第一期 问题描述 解析 代码实现 执行结果 第二期 问题描述 解析 3.C题&#xff08;数学问题&#xff09; 第一期 问题…

windows域控上批量修改域账号密码

目录 一、查询密码过期域账号信息 &#xff08;一&#xff09;根据OU组织架构查询密码过期账号 &#xff08;二&#xff09;查询域控所有密码过期账号 &#xff08;三&#xff09;导出dsquery查询的信息 二、批量修改过期域账号密码 &#xff08;一&#xff09;根据dsque…

【YOLO系列改进NO.47】改进激活函数为GELU

文章目录前言一、解决问题二、基本原理三、​添加方法四、总结前言 作为当前先进的深度学习目标检测算法YOLOv7&#xff0c;已经集合了大量的trick&#xff0c;但是还是有提高和改进的空间&#xff0c;针对具体应用场景下的检测难点&#xff0c;可以不同的改进方法。此后的系列…

Go-Excelize API源码阅读(四十)——SetCellRichText

Go-Excelize API源码阅读&#xff08;四十&#xff09;——SetCellRichText 开源摘星计划&#xff08;WeOpen Star&#xff09; 是由腾源会 2022 年推出的全新项目&#xff0c;旨在为开源人提供成长激励&#xff0c;为开源项目提供成长支持&#xff0c;助力开发者更好地了解开…

Android 虚拟分区详解(二) 虚拟分区布局

文章目录0. 导读1. Android 传统 A/B 分区和动态分区布局2. Android 虚拟分区布局3. 虚拟分区的思考2.1 分区只有一套&#xff0c;如何实现 A/B 系统特性&#xff1f;2.2 部分分区还有 A/B 两套&#xff0c;只要一套不行吗&#xff1f;2.3 为什么不把所有分区都放到动态分区里&…

自动化运维工具-----Ansible

一、主流自动 1.1 Puppet Puppet 是早期的 Linux 自动化运维工具&#xff0c;是一种 Linux、Unix、Windows 平台的集中配置管理系统&#xff0c;发展至今目前已经非常成熟&#xff0c;可以批量管理远程服务器&#xff0c;模块丰富&#xff0c;配置复杂&#xff0c;基于 Ruby …

[附源码]Python计算机毕业设计Django楼盘销售管理系统

项目运行 环境配置&#xff1a; Pychram社区版 python3.7.7 Mysql5.7 HBuilderXlist pipNavicat11Djangonodejs。 项目技术&#xff1a; django python Vue 等等组成&#xff0c;B/S模式 pychram管理等等。 环境需要 1.运行环境&#xff1a;最好是python3.7.7&#xff0c;…

Netty系列(四):源码解读 backlog 参数作用

sun.nio.ch.ServerSocketChannelImpl#bind方法 在ServerSocketChannel接口中&#xff0c;有一个bind方法&#xff0c;这个方法的作用是将通道的套接字绑定到本地地址并配置套接字以侦听连接。即用于在套接字和本地地址之间建立关联。而一旦建立关联&#xff0c;套接字将保持绑…

html5期末大作业——HTML+CSS公益关爱残疾人( 6个页面)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

Metabase学习教程:系统管理-3

保持条理化 当用户、问题和仪表盘的数量不可避免地增加时&#xff0c;如何保持分析的组织性。 如果您想保持竞争力&#xff0c;您需要让组织中的人员访问他们需要的数据&#xff0c;以便做出更好的决策。然而&#xff0c;这种数据自主化的代价是不可避免的大量分析——这会使…

CentenOS安装使用Docker

1 先更新一下yum 执行 yum -y update 这样算完成 2 清理一下&#xff0c;原来可能安过的docker yum remove docker docker-common docker-selinux docker-engine 3 安装所需软件包 yum install -y yum-utils device-mapper-persistent-data lvm2 安装过的可以跳过 4 配置yum中…

Qt OpenGL 光照和键盘控制

这次教程中&#xff0c;我们将添加光照和键盘控制&#xff0c;它让程序看起来更美观。我将教大家如何使用键盘来移动场景中的对象&#xff0c;还会教大家在OpenGL场景中应用简单的光照&#xff0c;让我们的程序更加视觉效果更好且受我们控制。 程序运行时效果如下&#xff1a;…