JVM上篇之类加载子系统

目录

类加载子系统

内存结构

类的生命周期

类的加载过程

加载

加载class文件方式

连接

验证

验证阶段

准备

解析

初始化

类加载器

介绍

作用

分类

引导类加载器

自定义类加载器

ClassLoader

获取ClassLoader途径

双亲委派机制

介绍

执行流程

好处

打破双亲委派


类加载子系统

内存结构

Class文件

类加载子系统

运行时数据区

        方法区

        堆

        程序计数器

        虚拟机栈

        本地方法栈

执行引擎

本地方法接口

本地方法库

类的生命周期

        类从被加载到虚拟机内存中开始到卸载出内存为止,整个生命周期为7个阶段:加载、验证、准备、解析、初始化、使用、卸载;其中前三个阶段统称为连接

 卸载:jvm结束生命周期

类的加载过程

class文件需要加载到虚拟机之后才能运行和使用;主要分为三步:加载、连接、初始化;其中连接又可以分为三步:验证、准备、解析

 /**
 *示例代码
 */
public class HelloLoader {
    public static void main(String[] args) {
        System.out.println("Hello World!");
    }
}

加载

类加载过程的第一步;

主要为了完成3件事:

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

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

        3.在内存中生成一个代表该类的class对象,作为方法区这些数据的访问入口

加载class文件方式

        从本地系统中直接加载

        通过网络获取

        从压缩包中获取

        运行时计算生成

        其他文件生成(jsp、html)

        从数据库中提取

        从加密文件中获取

连接

验证

主要是为了确保class文件字节流中包含的信息符合规范,保证这些信息被当作代码运行后不会危害虚拟机自身的安全。

验证阶段

准备

为类变量分配内存且设置该类变量的默认初始值。

注意

        1.进行内存分配的仅包括类变量(静态变量),而不包括实例变量

        2.这里所设置的初始值"通常情况"下是数据类型默认的零值(如 0、0L、null、false 等),比如我们定义了public static int value=111 ,那么 value 变量在准备阶段的初始值就是 0 而不是 111(初始化阶段才会赋值)。特殊情况:比如给 value 变量加上了 final 关键字public static final int value=111 ,那么准备阶段 value 的值就被赋值为 111

解析

解析是虚拟机将常量池内的符号引用替换为直接引用的过程。

解析动作主要针对类或接口、字段、类方法、接口方法、方法类型、方法句柄、调用限制符等7种符号引用进行

初始化

        初始化阶段是执行初始化方法<clinit>()方法的过程,是类加载的最后一步,之后jvm才开始执行类中定义的程序代码。

        对于<clinit>()方法的调用,jvm会确保其在多线程环境的安全性,因为<clinit>()方法是带锁线程安全,所以在多线程环境下进行类初始化可能会引起线程堵塞,并且这种堵塞很难被发现

类加载器

介绍

类加载器是一个负责加载类的对象,用于实现类加载过程中加载这一步;每个Java类都有一个引用指向加载它的ClassLoader;数组类不是ClassLoader创建的,而是jvm直接生成

作用

系统加载class类型的文件主要分为3步:加载、连接、初始化;其中连接过程又可以分为3步:验证、准备、解析

类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识

分类

jvm支持两种类型的加载器。分别为:引导类加载器、自定义类加载器。

自定义加载器一般指程序开发中开发人员自定义的一类类加载器,但是jvm规范却没有这么定义,而是将所有派生于ClassLoader的类加载器都划分为自定义加载器。

引导类加载器

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

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

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

        并不继承自ava.lang.ClassLoader,没有父加载器。

        加载扩展类和应用程序类加载器,并指定为他们的父类加载器。

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

扩展类加载器(Extension ClassLoader)

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

        派生于ClassLoader类

        父类加载器为启动类加载器

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

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

        java语言编写,由sun.misc.LaunchersAppClassLoader实现

        派生于ClassLoader类

        父类加载器为扩展类加载器

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

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

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

自定义类加载器

为什么要自定义类加载器?

        隔离加载类

        修改类加载的方式

        扩展加载源

        防止源码泄漏

实现步骤:

        1.需要继承 ClassLoader抽象类

        2.自定义的类加载逻辑写在findClass()方法中

        3.在编写自定义类加载器时,如果没有太过于复杂的需求,可以直接继承URLClassLoader类

ClassLoader

ClassLoader类是一个抽象类,其后所有的类加载器都继承自ClassLoader。

获取ClassLoader途径

1.获取当前ClassLoader

clazz.getClassLoader()

2.获取当前线程上下文的ClassLoader

Thread.currentThread().getContextClassLoader()

3.获取系统的ClassLoader

ClassLoader.getSystemClassLoader()

4.获取调用者的ClassLoader

DriverManager.getCallerClassLoader()

双亲委派机制

介绍

        当一个类收到了加载请求时,它是不会先自己去尝试加载的,而是委派给父类去完成,比如我现在要 new 一个 Person,这个 Person 是我们自定义的类,如果我们要加载它,就会先委派 App ClassLoader ,只有当父类加载器都反馈自己无法完成这个请求(也就是父类加载器都没有找到加载所需的 Class)时,子类加载器才会自行尝试加载。

        双亲委派模型并不是一种强制性的约束,只是 JDK 官方推荐的一种方式

执行流程

        1.在类加载的时候,系统会首先判断当前类是否被加载过。已经被加载的类会直接返回,否则才会尝试加载

        2.类加载器在进行类加载的时候,它首先不会自己去尝试加载这个类,而是把这个请求委派给父类加载器去完成

        3.只有当父加载器反馈自己无法完成这个加载请求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去加载(调用自己的 findClass() 方法来加载类)

protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException
{synchronized (getClassLoadingLock(name)) {//首先,检查该类是否已经加载过Class c = findLoadedClass(name);if (c == null) {//如果 c 为 null,则说明该类没有被加载过long t0 = System.nanoTime();try {if (parent != null) {//当父类的加载器不为空,则通过父类的loadClass来加载该类c = parent.loadClass(name, false);} else {//当父类的加载器为空,则调用启动类加载器来加载该类c = findBootstrapClassOrNull(name);}} catch (ClassNotFoundException e) {//非空父类的类加载器无法找到相应的类,则抛出异常}if (c == null) {//当父类加载器无法加载时,则调用findClass方法来加载该类//用户可通过覆写该方法,来自定义类加载器long t1 = System.nanoTime();c = findClass(name);//用于统计类加载器相关的信息sun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0);sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1);sun.misc.PerfCounter.getFindClasses().increment();}}if (resolve) {//对类进行link操作resolveClass(c);}return c;}
}

好处

        避免类的重复加载

        保护程序安全,防止核心api被篡改

打破双亲委派

        自定义加载器的话,需要继承 ClassLoader 。如果我们不想打破双亲委派模型,就重写 ClassLoader 类中的 findClass() 方法即可,无法被父类加载器加载的类最终会通过这个方法被加载。但是,如果想打破双亲委派模型则需要重写 loadClass() 方法。

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

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

常见的Web安全漏洞(2021年9月的OWASP TOP 10)

聊Web安全漏洞&#xff0c;就不得不提到OWASP TOP10。开放式Web应用程序安全项目&#xff08;OpenWeb Application Security Project&#xff0c;OWASP&#xff09;是一个开源的、非营利的组织&#xff0c;主要提供有关Web应用程序的实际可行、公正透明、有社会效益的信息&…

Jenkins 执行远程shell脚本部署jar文件问题起不来

如图&#xff1a;最开始的时候没有加&#xff1a; source /etc/profile 这一行&#xff0c; run.sh里面的java -jar xxxx.jar 一直执行不来。 一开始以为是Jenkins执行退出后会kill一切它启动的进程&#xff0c;所以加了在run.sh里面加了export BUILD_IDdontKillMe&#xff0…

42. QT中开发Android配置QFtp功能时遇到的编译问题

1. 说明 此问题仅适用在QT中开发Android程序时&#xff0c;需要适用QFtp功能的情况。一般情况下&#xff0c;如果开发的是Windows或者Linux系统下的程序&#xff0c;可能不会出现该问题。 2. 问题 【Android】在将QFtp的相关代码文件加入到项目中后&#xff0c;编译项目时会…

Python 中的 set 集合类型是可迭代的吗?

当我们运行以下代码时会报错。 a {1, 2, 4, 3, 4} for i in range(len(a)):print(a[i]) 所以我之前一直以为 set 类型是不可迭代的&#xff0c;后来发现这里的报错问题是&#xff1a;set object is not subscriptable&#xff0c;也就是说 set 是不可以通过下标来访问的。因为…

uniapp:swiper-demo效果

单元格轮播 <swiper class"swiper1" :circular"true" :autoplay"true" interval"3000" previous-margin"195rpx" next-margin"195rpx"><swiper-item v-for"(item,index) in 5" :key"inde…

华为、小鹏大定爆单,智驾苦尽甘来,车主终于愿意买单

‍作者|德新 编辑|王博 国庆假期结束&#xff0c;车圈的最大热点事件&#xff0c;当属问界M7卖爆&#xff0c;上市不到一个月时间内&#xff0c;狂揽5万张大定订单。 在华为手机强势回归&#xff0c;改款问界M7大热的高光之下&#xff0c;还有一个重要趋势值得关注&#xff1…

AIGC|利用大语言模型实现智能私域问答助手

随着ChatGPT的爆火&#xff0c;最近大家开始关注到大语言模型&#xff08;LLM&#xff09;这个领域。像雨后春笋一样&#xff0c;国内外涌现出了很多LLM。作为开发者&#xff0c;我们通常会关注LLM各自擅长的领域和能力&#xff0c;然后思考如何利用它们的能力来解决某个场景或…

大数据——Spark Streaming

是什么 Spark Streaming是一个可扩展、高吞吐、具有容错性的流式计算框架。 之前我们接触的spark-core和spark-sql都是离线批处理任务&#xff0c;每天定时处理数据&#xff0c;对于数据的实时性要求不高&#xff0c;一般都是T1的。但在企业任务中存在很多的实时性的任务需求&…

Docker 网络访问原理解密

How Container Networking Works: Practical Explanation 这篇文章讲得非常棒&#xff0c;把docker network讲得非常清晰。 分为三个部分&#xff1a; 1&#xff09;docker 内部容器互联。 2&#xff09;docker 容器 访问 外部root 网络空间。 3&#xff09;外部网络空间…

读书笔记:多Transformer的双向编码器表示法(Bert)-4

多Transformer的双向编码器表示法 Bidirectional Encoder Representations from Transformers&#xff0c;即Bert&#xff1b; 第二部分 探索BERT变体 从本章开始的诸多内容&#xff0c;以理解为目标&#xff0c;着重关注对音频相关的支持&#xff08;如果有的话&#xff09;…

解读非托管流动性协议Hover: 差异化、层次化的全新借贷体系

“Hover 是 DeFi 借贷赛道的另辟蹊径者&#xff0c;除了在自身机制&#xff08;借贷模型、治理体系&#xff09;上进行创新获得内生动力外&#xff0c;背靠日渐繁荣的 Kava、Cosmos 生态进一步获得外生动力&#xff0c;发展潜力俱佳” 与 DEX 类似&#xff0c;借贷也是 DeFi 世…

小谈设计模式(23)—桥接模式

小谈设计模式&#xff08;23&#xff09;—桥接模式 专栏介绍专栏地址专栏介绍 桥接模式主要角色抽象部分实现部分分析 核心思想应用场景123 优缺点分析优点123 缺点12 总结 专栏介绍 专栏地址 link 专栏介绍 主要对目前市面上常见的23种设计模式进行逐一分析和总结&#x…