Java笔记简要总结-类在Java虚拟机中如何玩耍的

2024-01-15 05:59

本文主要是介绍Java笔记简要总结-类在Java虚拟机中如何玩耍的,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在这里插入图片描述

🍎 博客主页:@风一样的美狼子
🍎 欢迎关注:👍点赞🍃收藏🔥留言
🍎系列专栏: 《云平台实战》、《Linux随你玩-实操》
🍎在阳光下灿烂,风雨中奔跑,泪水中成长,拼搏中展望。🍎
🍎一起加油,去追寻、去成为更好的自己!🍎

文章目录

  • 前言
  • 1、类
    • 1.1、类装载的执行过程
    • 1.2、类加载器种类以及加载范围
      • 1.2.1、启动类加载器(Bootstrap ClassLoader)
      • 1.2.2、扩展类加载器(Extension ClassLoader)
      • 1.2.3、应用程序类加载器(Application ClassLoader)
      • 1.2.4、自定义类加载器(User ClassLoader)
      • 1.2.5、模块化中的类加载器
        • 1.2.5.1、JDK9中类加载器的变化
        • 1.2.5.2、OSGi模块化服务
    • 1.3、 双亲委派是什么
    • 1.4、为什么需要有双亲委派呢
    • 1.5、既然需要有双亲委派为何还要破坏双亲委派模型呢
    • 1.6、如何破坏双亲委派模型
    • 1.7、如何自定义一个类加载器
    • 1.8、 热部署原理
    • 1.9、结语

前言

     JVM是Java Virtual Machine(Java虚拟机)的缩写,JVM是一种用于计算设备的规范,它是一个虚构出来的计算机,那我们使用的类是如何进入JVM 这个神秘世界的了?它在里面都发生了什么?今天我就带领大家揭晓它的 神秘面纱,GO!

1、类

1.1、类装载的执行过程

类装载分为以下 5 个步骤:

  1. 加载: 根据查找路径找到相应的 class 文件中的二进制数据读入到内存中,并为之创建一个java.lang.Class对象; 通过使用不同的类加载器可以从不同来源加载类的二进制数据,
       通常有如下几种来源:
          a.从本地文件系统加载
          b.从jar包加载
          c.通过网络加载
          d.把一个Java源文件动态编译,并执行加载
  2. 检查: 检查加载的 class 文件的正确性;(文件格式验证、元数据验证、字节码验证、符号引用验证)
  3. 准备: 给类中的静态变量分配内存空间,并设置默认初始值;
  4. 解析: 虚拟机将常量池中的符号引用替换成直接引用的过程。符号引用就理解为一个标示,> >而在直接引用直接指向内存中的地址;
  5. 初始化: 对静态变量和静态代码块执行初始化工作。

类其实还有其它过程:

使用: 对象都出来了,业务系统直接调用阶段。
卸载: 用完了,可以被GC回收了。

1.2、类加载器种类以及加载范围

类加载器负责加载所有的类,其为所有被载入内存的类生成一个java.lang.Class实例对象。
在这里插入图片描述

1.2.1、启动类加载器(Bootstrap ClassLoader)

最顶层类加载器,该类没有父加载器,用来加载Java的核心类,启动类加载器的实现依赖于底层操作系统,属于虚拟机实现的一部分,它并不继承自java.lang.classLoader。
负责加载jvm的核心类库,比如java.lang.*等,从系统属性中的sun.boot.class.path所指定的目录中加载类库。他的具体实现由Java虚拟机底层C++代码实现。

1.2.2、扩展类加载器(Extension ClassLoader)

它的父类为启动类加载器,扩展类加载器是纯java类,是ClassLoader类的子类,负责加载JRE的扩展目录。

从java.ext.dirs系统属性所指定的目录中加载类库,或者从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库,如果把用户的jar文件放在这个目录下,也会自动由扩展类加载器加载。继承java.lang.ClassLoader。

1.2.3、应用程序类加载器(Application ClassLoader)

它的父类为扩展类加载器,它从环境变量classpath或者系统属性java.lang.path所指定的目录中加载类,继承自java.lang.ClassLoader。

1.2.4、自定义类加载器(User ClassLoader)

除了上面三个自带的以外,用户还能制定自己的类加载器,但是所有自定义的类加载器都应该继承自java.lang.ClassLoader。比如热部署、tomcat都会用到自定义类加载器。

1.2.5、模块化中的类加载器

JDK9开始引入模块化,是为了能够实现模块化的“可配置封装隔离机制”
某个类库到底是在模块还是在传统的jar包,只取决于他存放在哪种路径上
在这之前,如果类路径中确实了运行时依赖的类型,那就只能等程序运行到发生该类型的加载,连接时才会报运行异常。
在JDK9之后,如果启用了模块化进行封装,模块就可以声明对其他模块的显式依赖,这样JVM就能够在启动时验证应用程序的完备性。

1.2.5.1、JDK9中类加载器的变化

1、扩展类加载器(Extension Class Loader)被平台类加载器(Platform Class Loader)取代。
2、Java类库不再保留\lib\ext,JDK已基于模块化进行构建(原来的rt.jar和tools.jar被拆分成数十个JMOD)。
3、取消了\jre目录,因为随时可以组合构建出程序运行所需的jre,如我们只需要使用java.base模型中的类型,那么随时可以打包出一个jre,需要如下命令:
jlink -p $JAVA_HOME/jmods --add-modules java.base --output jre
4、平台类加载器(Platform Class Loader)和应用类加载器(Application Class
Loader)都不再派生自java.net.URLClassLoader,而全部继承jdk.internal.loader.BuiltinClassLoader。
5、当平台及应用程序类加载器收到类加载请求,在委派给父加载器加载前,先判断该类是否能够归属到某个系统模块中,如果可以找到归属系统模块,就优先委派给负责那个模块的加载器加载。
因此模块化中的类加载委派关系如下:(与三层类加载器图对比)

在这里插入图片描述

1.2.5.2、OSGi模块化服务

在JDK9之后OSGi的热部署成为当下流行的一项优势。
它通过自定义类加载机制实现,每一个程序模块(Bundle)都有一个属于自己的类加载器,当需要更换一个Bundle时,就把Bundle联通类加载器一起换掉,以实现热替换
在此环境下,类加载器不再需要双亲委派模型的树状结构,而是进一步发展为更加复杂的网状结构。

1.3、 双亲委派是什么

如果一个类加载器收到了类加载的请求,他首先会从自己缓存里查找是否之前加载过这个class,加载过直接返回,没加载过的话他会把这个请求委派给父类加载器去完成,每一层都是如此,类似递归,一直递归到顶层父类。
也就是Bootstrap ClassLoader,只要加载完成就会返回结果,如果顶层父类加载器无法加载此class,则会返回去交给子类加载器去尝试加载,若最底层的子类加载器也没找到,则会抛出ClassNotFoundException。

1.4、为什么需要有双亲委派呢

主要还是防止内存中出现多份同样的字节码,安全

比如自己重写个java.lang.Object并放到Classpath中,没有双亲委派的话直接自己执行了,那不安全。双亲委派可以保证这个类只能被顶层Bootstrap Classloader类加载器加载,从而确保只有JVM中有且仅有一份正常的java核心类。如果有多个的话,那么就乱套了。比如相同的类instance
of可能返回false,因为可能父类不是同一个类加载器加载的Object。

1.5、既然需要有双亲委派为何还要破坏双亲委派模型呢

Jdbc为什么要破坏双亲委派模型?
以前的用法是未破坏双亲委派模型的,比如Class.forName(“com.mysql.cj.jdbc.Driver”);

而在JDBC4.0以后,开始支持使用spi的方式来注册这个Driver,具体做法就是在mysql的jar包中的META-INF/services/java.sql.Driver文件中指明当前使用的Driver是哪个,然后使用的时候就不需要我们手动的去加载驱动了,我们只需要直接获取连接就可以了。
Connection con = DriverManager.getConnection(url, username, password);

为什么JDBC需要破坏双亲委派模式呢?
**原因是原生的JDBC中Driver驱动本身只是一个接口,并没有具体的实现,具体的实现是由不同数据库类型去实现的。**例如,MySQL的mysql-connector-*.jar中的Driver类具体实现的。

原生的JDBC中的类是放在rt.jar包的,是由Bootstrap加载器进行类加载的,在JDBC中的Driver类中需要动态去加载不同数据库类型的Driver类,而mysql-connector-*.jar中的Driver类是用户自己写的代码,那Bootstrap类加载器肯定是不能进行加载的,既然是自己编写的代码,那就需要由Application类加载器去进行类加载。
这个时候就引入线程上下文件类加载器(Thread Context ClassLoader),通过这个东西程序就可以把原本需要由Bootstrap类加载器进行加载的类由Application类加载器去进行加载了。

Tomcat为什么要破坏双亲委派模型?
因为一个Tomcat可以部署N个web应用,但是每个web应用都有自己的classloader,互不干扰

如web1里面有com.test.A.class,web2里面也有com.test.A.class,如果没打破双亲委派模型的话,那么web1加载完后,web2在加载的话会冲突。
因为只有一套classloader,却出现了两个重复的类路径,所以tomcat打破了,他是线程级别的,不同web应用是不同的classloader。
Java spi 方式,比如jdbc4.0开始就是其中之一。
热部署的场景会破坏,否则实现不了热部署。

1.6、如何破坏双亲委派模型

重写loadClass方法,别重写findClass方法,因为loadClass是核心入口,将其重写成自定义逻辑即可破坏双亲委派模型。

1.7、如何自定义一个类加载器

只需要继承java.lang.Classloader类,然后覆盖他的findClass(String name)方法即可,该方法根据参数指定的类名称,返回对应 的Class对象的引用。

1.8、 热部署原理

采取破坏双亲委派模型的手段来实现热部署,默认的loadClass()方法先找缓存,你改了class字节码也不会热加载,所以自定义ClassLoader,去掉找缓存那部分,直接就去加载,也就是每次都重新加载。

1.9、结语

各位看官今天就到此为止,此系列简要笔记后续会不断更新,🍎 欢迎关注:👍点赞🍃收藏🔥留言!
在这里插入图片描述

这篇关于Java笔记简要总结-类在Java虚拟机中如何玩耍的的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/607885

相关文章

SpringBoot条件注解核心作用与使用场景详解

《SpringBoot条件注解核心作用与使用场景详解》SpringBoot的条件注解为开发者提供了强大的动态配置能力,理解其原理和适用场景是构建灵活、可扩展应用的关键,本文将系统梳理所有常用的条件注... 目录引言一、条件注解的核心机制二、SpringBoot内置条件注解详解1、@ConditionalOn

通过Spring层面进行事务回滚的实现

《通过Spring层面进行事务回滚的实现》本文主要介绍了通过Spring层面进行事务回滚的实现,包括声明式事务和编程式事务,具有一定的参考价值,感兴趣的可以了解一下... 目录声明式事务回滚:1. 基础注解配置2. 指定回滚异常类型3. ​不回滚特殊场景编程式事务回滚:1. ​使用 TransactionT

Spring LDAP目录服务的使用示例

《SpringLDAP目录服务的使用示例》本文主要介绍了SpringLDAP目录服务的使用示例... 目录引言一、Spring LDAP基础二、LdapTemplate详解三、LDAP对象映射四、基本LDAP操作4.1 查询操作4.2 添加操作4.3 修改操作4.4 删除操作五、认证与授权六、高级特性与最佳

Spring Shell 命令行实现交互式Shell应用开发

《SpringShell命令行实现交互式Shell应用开发》本文主要介绍了SpringShell命令行实现交互式Shell应用开发,能够帮助开发者快速构建功能丰富的命令行应用程序,具有一定的参考价... 目录引言一、Spring Shell概述二、创建命令类三、命令参数处理四、命令分组与帮助系统五、自定义S

SpringSecurity JWT基于令牌的无状态认证实现

《SpringSecurityJWT基于令牌的无状态认证实现》SpringSecurity中实现基于JWT的无状态认证是一种常见的做法,本文就来介绍一下SpringSecurityJWT基于令牌的无... 目录引言一、JWT基本原理与结构二、Spring Security JWT依赖配置三、JWT令牌生成与

Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码

《Java中Date、LocalDate、LocalDateTime、LocalTime、时间戳之间的相互转换代码》:本文主要介绍Java中日期时间转换的多种方法,包括将Date转换为LocalD... 目录一、Date转LocalDateTime二、Date转LocalDate三、LocalDateTim

如何配置Spring Boot中的Jackson序列化

《如何配置SpringBoot中的Jackson序列化》在开发基于SpringBoot的应用程序时,Jackson是默认的JSON序列化和反序列化工具,本文将详细介绍如何在SpringBoot中配置... 目录配置Spring Boot中的Jackson序列化1. 为什么需要自定义Jackson配置?2.

Java中使用Hutool进行AES加密解密的方法举例

《Java中使用Hutool进行AES加密解密的方法举例》AES是一种对称加密,所谓对称加密就是加密与解密使用的秘钥是一个,下面:本文主要介绍Java中使用Hutool进行AES加密解密的相关资料... 目录前言一、Hutool简介与引入1.1 Hutool简介1.2 引入Hutool二、AES加密解密基础

Spring Boot项目部署命令java -jar的各种参数及作用详解

《SpringBoot项目部署命令java-jar的各种参数及作用详解》:本文主要介绍SpringBoot项目部署命令java-jar的各种参数及作用的相关资料,包括设置内存大小、垃圾回收... 目录前言一、基础命令结构二、常见的 Java 命令参数1. 设置内存大小2. 配置垃圾回收器3. 配置线程栈大小

SpringBoot实现微信小程序支付功能

《SpringBoot实现微信小程序支付功能》小程序支付功能已成为众多应用的核心需求之一,本文主要介绍了SpringBoot实现微信小程序支付功能,文中通过示例代码介绍的非常详细,对大家的学习或者工作... 目录一、引言二、准备工作(一)微信支付商户平台配置(二)Spring Boot项目搭建(三)配置文件