【实战JVM】-基础篇-02-类的声明周期-加载器

【实战JVM】-基础篇-02-类的声明周期-加载器

  • 3 类的生命周期
    • 3.1 生命周期的概述
    • 3.2 加载阶段
      • 3.2.1 查看内存中的对象
    • 3.3 连接阶段
      • 3.3.1 验证阶段
        • 3.3.1.1 验证是否符合jvm规范
        • 3.3.1.2 元信息验证
        • 3.3.1.3 验证语义
        • 3.3.1.4 符号引用验证
      • 3.3.2 准备阶段
      • 3.3.3 解析阶段
    • 3.4 初始化阶段
      • 3.4.1 笔试题
      • 3.4.2 特殊情况
    • 3.5 总结
  • 4 类的加载器
    • 4.1 类加载器的分类
      • 4.1.1 JDK8之前的分类
      • 4.1.2 使用Arthas查看类加载器-classloader
      • 4.1.3 C++启动类加载器BootstrapClassLoader
      • 4.1.4 Java中默认类加载器
        • 4.1.4.1 扩展类加载器ExtClassLoader
        • 4.1.4.2 应用程序类加载器 AppClassLoader
        • 4.1.4.3 Arthas-classloader高级用法
    • 4.2 类加载器的双亲委派机制
      • 4.2.1 Arthas查看类加载器父子关系
      • 4.2.2 面试
    • 4.3 打破双亲委派机制
      • 4.3.1 自定义类加载器
        • 4.3.1.1 Arthas展示类的详细信息
        • 4.3.1.2 正确的自定义类加载器
      • 4.3.2 线程上下文类加载器
        • 4.3.2.1 SPI机制
        • 4.3.2.2 总结
      • 4.3.3 热部署
        • 4.3.3.1 热更新注意事项
    • 4.4 JDK8之后的类加载器


3 类的生命周期

3.1 生命周期的概述

在这里插入图片描述

3.2 加载阶段

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

3.2.1 查看内存中的对象

推荐使用JDK自带的hsdb工具查看Java虚拟机内部的内存信息。工具位于JDK安装目录下的lib文件夹的sa-jdi.jar中。

启动jvm项目的HsdbDemo

在这里插入图片描述

使用jps展示当前所有的java进程及id

jps

在这里插入图片描述

启动命令

D:\Software\software_with_code\idea\jdk\jdk1.8.0_381\lib>java -cp sa-jdi.jar sun.jvm.hotspot.HSDB

在这里插入图片描述

输入我们要找的类的编号HsdbDemo:18648

在这里插入图片描述

直接搜索HsdbDemo,因为我们只new了一次,自然只有一个对象。

在这里插入图片描述

在这里插入图片描述

一句话概括:类加载器将类的信息加载到内存中,java虚拟机在方法区和堆区中各分配一个对象去保存这个信息,而我们需要操作的则是堆区中的对象,jdk8之后静态字段也是存在堆中的

3.3 连接阶段

在这里插入图片描述

3.3.1 验证阶段

3.3.1.1 验证是否符合jvm规范

在这里插入图片描述

3.3.1.2 元信息验证

在这里插入图片描述

3.3.1.3 验证语义

在这里插入图片描述

3.3.1.4 符号引用验证

判断是否访问了其他类的private方法。

3.3.2 准备阶段

在这里插入图片描述
value在准备阶段分配的值是默认值0,而赋值为1是初始化阶段做的事

在这里插入图片描述

但是也有例外,如果是final修饰的基本数据类型,会在准备阶段直接将代码中的值进行赋值。

public static final int value=1;

3.3.3 解析阶段

在这里插入图片描述

直接引用不在使用编号,而是直接使用内存中的地址进行访问具体的数据。

3.4 初始化阶段

  • 初始化阶段会执行静态代码中的代码,并为静态变量赋值
  • 初始化阶段会执行字节码文件中的clinit部分的字节码指令。

在这里插入图片描述

如果颠倒一下顺序,那么输出则是1

在这里插入图片描述

因为静态变量是在连接阶段准备阶段完成默认初始化。然后再赋为2,再赋为1。

在这里插入图片描述

3.4.1 笔试题

(1)

在这里插入图片描述

构造代码块先于构造方法前执行

3.4.2 特殊情况

在这里插入图片描述

  • 直接访问父类的静态变量,不会触发子类的初始化。

  • 子类的初始化clinit调用之前,会先调用父类的clinit初始化方法。
    在这里插入图片描述

  • 数组的创建不会导致数组中元素的类进行初始化。

    • 是因为创建数组时是创建的数组的对象,而不是数组中元素的对象。所以数组中元素的类不会进行初始化。
  • 如果一个变量用final修饰,并且其中的内容要执行指令才能得出结果,那么会在clinit方法中进行初始化。

3.5 总结

在这里插入图片描述

4 类的加载器

类加载器是jvm提供给应用程序去实现获取类和接口字节码数据的技术。

负责在类加载过程中的字节码获取并且加载到内存这一部分。通过加载字节码数据放入内存转换成byte[],接下来调用虚拟机底层的方法将byte[]转换成方法区和堆中的数据。

4.1 类加载器的分类

在这里插入图片描述

俩下Shift是搜索
Ctrl+Alt+类是找当前类的所有实现

4.1.1 JDK8之前的分类

在这里插入图片描述

  • 引导类加载器 Bootstrap,加载属于JVM的一部分,由C++代码实现,负责加载<JAVA_HOME\>\jre\lib路径下的核心类库
  • 扩展类加载器 ExtClassLoader,扩展类加载器负责加载<JAVA_HOME>\jre\lib\ext目录下的类库。
  • 应用程序类加载器 AppClassLoader,应用程序类加载器负责加载 classpath环境变量所指定的类库,是用户自定义类的默认类加载器。

4.1.2 使用Arthas查看类加载器-classloader

在这里插入图片描述

启动Hsdbdemo后打开arthas,在arthas工作目录中启动

java -jar arthas-boot.jar

进入Hsdbdemo

classloader

在这里插入图片描述

4.1.3 C++启动类加载器BootstrapClassLoader

负责加载<JAVA_HOME\>\jre\lib路径下的核心类库

通过类名.class.getClassLoader来获取当前类的类加载器。

在这里插入图片描述

添加java虚拟机参数D:/jvm/jar/classloader-test.jar是jar包地址

-Xbootclasspath/a:D:/jvm/jar/classloader-test.jar

4.1.4 Java中默认类加载器

在这里插入图片描述

4.1.4.1 扩展类加载器ExtClassLoader

扩展类加载器 ExtClassLoader,扩展类加载器负责加载<JAVA_HOME>\jre\lib\ext目录下的类库。

在这里插入图片描述

添加java虚拟机参数D:/jvm/jar/classloader-test.jar是jar包地址。

不仅需要jar包的地址,还需要原来ext的地址D:\Software\software_with_code\idea\jdk\jdk1.8.0_381\jre\lib\ext

-Djava.ext.dirs="D:\Software\software_with_code\idea\jdk\jdk1.8.0_381\jre\lib\ext;D:/jvm/jar/classloader-test.jar"

在windows中;是追加,linux和mac中:是追加,尽量用双引号引起俩,以免因为特殊字符报错。

4.1.4.2 应用程序类加载器 AppClassLoader

应用程序类加载器 AppClassLoader,应用程序类加载器负责加载 classpath环境变量所指定的类库,是用户自定义类的默认类加载器。既可以加载当前项目中创建的类,也可以加载maven依赖中包含的类。

4.1.4.3 Arthas-classloader高级用法
classloader -l

在这里插入图片描述

查看当前所有的类加载器以及其哈希值

classloader -c hash值

在这里插入图片描述

查看当前查询的类加载器加载的所有jar包

在这里插入图片描述

4.2 类加载器的双亲委派机制

jvm中有多个类加载器,双亲委派机制的核心就是解决一个类到底由谁加载的问题。

双亲委派机制的作用

  1. 保证类加载的安全性

    通过双亲委派机制避免恶意代码替换JDK中的核心类库,比如Java.lang.String,确保核心类库的完整性和安全性。

  2. 避免重复加载

    双亲委派机制可以避免同一个类被多次加载

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.2.1 Arthas查看类加载器父子关系

classloader -t

在这里插入图片描述

4.2.2 面试

在这里插入图片描述

4.3 打破双亲委派机制

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

4.3.1 自定义类加载器

在这里插入图片描述

@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
    if(name.startsWith("java.")){
        return super.loadClass(name);
    }
    byte[] data = loadClassData(name);
    return defineClass(name, data, 0, data.length);
}

public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, IOException {
    BreakClassLoader1 classLoader1 = new BreakClassLoader1();
    classLoader1.setBasePath("D:\\lib\\");
    Class<?> clazz1 = classLoader1.loadClass("com.itheima.my.A");
    BreakClassLoader1 classLoader2 = new BreakClassLoader1();
    classLoader2.setBasePath("D:\\lib\\");
    Class<?> clazz2 = classLoader2.loadClass("com.itheima.my.A");
    System.out.println(clazz1 == clazz2);
    Thread.currentThread().setContextClassLoader(classLoader1);
    System.out.println(Thread.currentThread().getContextClassLoader());
    System.in.read();
 }

重写loadClass方法,删除双亲委派机制,如果是java开头的jar包,就交给原先父类的loadClass,如果是自定义的,就自己直接加载。

自定义类加载器没指定双亲的话,默认双亲为应用程序类加载器

在这里插入图片描述

4.3.1.1 Arthas展示类的详细信息
sc -d com.itheima.my.A

在这里插入图片描述

因为刚刚用两个不同的类加载器加载com.itheima.my.A,自然得到两个不同的对象

4.3.1.2 正确的自定义类加载器

在这里插入图片描述

4.3.2 线程上下文类加载器

在这里插入图片描述

DriverManager类位于rt.jar包中,由启动类加载器加载。

在这里插入图片描述

4.3.2.1 SPI机制

SPI全程Service Provider Interface,是JDK内置的一种服务提供发现的机制

需要在resources目录下新建META-INF/services目录,并且在这个目录下新建一个与上述接口的全限定名一致的文件java.sql.Driver的接口,在这个文件中写入接口的实现类的全限定名com.mysql.cj.jdbc.Driver。

在这里插入图片描述

ServiceLoader这个类的源码如下:

public final class ServiceLoader<S> implements Iterable<S> {
    //扫描目录前缀
    private static final String PREFIX = "META-INF/services/";
    // 被加载的类或接口
    private final Class<S> service;
    // 用于定位、加载和实例化实现方实现的类的类加载器
    private final ClassLoader loader;
    // 上下文对象
    private final AccessControlContext acc;
    // 按照实例化的顺序缓存已经实例化的类
    private LinkedHashMap<String, S> providers = new LinkedHashMap<>();
    // 懒查找迭代器
    private java.util.ServiceLoader.LazyIterator lookupIterator;
    // 私有内部类,提供对所有的service的类的加载与实例化
    private class LazyIterator implements Iterator<S> {
        Class<S> service;
        ClassLoader loader;
        Enumeration<URL> configs = null;
        String nextName = null;
        //...
        private boolean hasNextService() {
            if (configs == null) {
                try {
                    //获取目录下所有的类
                    String fullName = PREFIX + service.getName();
                    if (loader == null)
                        configs = ClassLoader.getSystemResources(fullName);
                    else
                        configs = loader.getResources(fullName);
                } catch (IOException x) {
                    //...
                }
                //....
            }
        }
        private S nextService() {
            String cn = nextName;
            nextName = null;
            Class<?> c = null;
            try {
                //反射加载类
                c = Class.forName(cn, false, loader);
            } catch (ClassNotFoundException x) {
            }
            try {
                //实例化
                S p = service.cast(c.newInstance());
                //放进缓存
                providers.put(cn, p);
                return p;
            } catch (Throwable x) {
                //..
            }
            //..
        }
    }
}

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在线程中使用

Thread.currentThread().getContextClassLoader()

默认获得的是应用程序类加载器

4.3.2.2 总结

在这里插入图片描述

在这里插入图片描述

所以说,打破双亲委派机制的唯一方法就是重写loadClass或者findClass方法

4.3.3 热部署

在这里插入图片描述

  1. 启动SpringbootClassfileApplication后,使用arthas进入,反编译出UserController

    java -Dfile.encoding=UTF-8 -jar arthas-boot.jar
    
    jad --source-only com.itheima.springbootclassfile.controller.UserController > "D:\Code\JavaCode\JVM\hot-replace\UserController.java"
    

在这里插入图片描述

修改为

if (type.equals(UserType.REGULAR.getType())) {
  1. 编译成字节码文件,如果直接编译,则会因为找不到类加载器而报错,所以需要先找到UserController.java的类加载器,获取其哈希值。

    sc -d com.itheima.springbootclassfile.controller.UserController
    

    在这里插入图片描述

  2. mc -c指定类加载器的哈希码 -d指定输出目录

    mc -c 18b4aac2 "D:\Code\JavaCode\JVM\hot-replace\UserController.java" -d "D:\Code\JavaCode\JVM\hot-replace"
    

    在这里插入图片描述

  3. 通过retransform

    retransform "D:\Code\JavaCode\JVM\hot-replace\com\itheima\springbootclassfile\controller\UserController.class"
    

    在这里插入图片描述

  4. 用jad查看热部署是否完成

    在这里插入图片描述

  5. 发送http请求

    在这里插入图片描述

    说明热部署已经完成

4.3.3.1 热更新注意事项

在这里插入图片描述

4.4 JDK8之后的类加载器

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

应用类加载器和扩展类加载器都是继承关系从URLClassLoader变为BuiltinClassLoader,没有太大的区别。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

LangChain实战技巧之三:关于Tool的一点拓展

&#xff08;几乎&#xff09;任一LLM在bind_tools时&#xff0c;都是习惯先定义一个Function或BaseTool&#xff0c;然后再bind&#xff08;bind_tools&#xff09;具体方式可参考我的这篇文章 AI菜鸟向前飞 — LangChain系列之十三 - 关于Tool的必知必会 但这里的tool未必需…

Python代码:十九、列表的长度

1、题目 描述&#xff1a; 牛牛学会了使用list函数与split函数将输入的连续字符串封装成列表&#xff0c;你能够帮他使用len函数统计一些公输入了多少字符串&#xff0c;列表中有多少元素吗&#xff1f; 输入描述&#xff1a; 输入一行多个字符串&#xff0c;字符串之间通过…

猫狗分类识别模型建立①数据标记

一、labelImg库说明 LabelImg是一款非常流行的图像标注工具&#xff0c;广泛用于机器学习和计算机视觉领域。以下是关于LabelImg的详细介绍&#xff1a; 主要功能和特点 1.图像标注 允许用户在图像中标注物体&#xff0c;选择特定区域&#xff0c;并为这些区域添加标签或类…

虚拟列表 vue-virtual-scroller 的使用

npm 详情&#xff1a;vue-virtual-scroller - npm (npmjs.com) 这里我使用的是RecycleScroller。 App.vue <template><RecycleScrollerclass"scroller":items"items":item-size"54"v-slot"{ item }"><list-item :it…

Vue框架动态引入省份个性化代码

项目需求有产品的功能&#xff0c;但是功能下部分小功能每个省份有不同的控制&#xff0c;所以需要引入省份个性化代码。 思路是&#xff0c;页面一开始加载产品化的代码&#xff0c;有个性化的代码就加载个性化的逻辑&#xff0c;个性化代码是产品化代码的重写&#xff0c;所…

网络协议——FTP(简介、搭建FTP服务端)

一、简介 1、什么是FTP&#xff1f; FTP&#xff08;File Transfer Protocol&#xff0c;文件传输协议&#xff09; TCP/IP 协议组的协议之一。常用20&#xff08;数据&#xff09;、21&#xff08;命令&#xff09;端口作为通讯端口。&#xff08;22为SSH端口&#xff09;F…

机器学习入门指南:Jupyter Notebook实战

前言 机器学习作为人工智能领域的核心组成&#xff0c;是计算机程序学习数据经验以优化自身算法、并产生相应的“智能化的“建议与决策的过程。随着大数据和 AI 的发展&#xff0c;越来越多的场景需要 AI 手段解决现实世界中的真实问题&#xff0c;并产生我们所需要的价值。 机…

引力为什么会让时间变慢,给你通俗的解读

爱因斯坦的狭义相对论表明&#xff0c;速度会让时间变慢&#xff0c;速度越快时间就越慢。而广义相对论告诉我们&#xff0c;引力同样会让时间变慢&#xff0c;引力越强时间就越慢。 时间膨胀 速度对时间的影响就先不解释了&#xff0c;之前的科普文章介绍了很多&#xff0c;今…

【C++】从零开始map与set的封装

送给大家一句话&#xff1a; 今日的事情&#xff0c;尽心、尽意、尽力去做了&#xff0c;无论成绩如何&#xff0c;都应该高高兴兴地上床恬睡。 – 三毛 《亲爱的三毛》 &#x1f303;&#x1f303;&#x1f303;&#x1f303;&#x1f303;&#x1f303;&#x1f303;&#x…

看潮成长日程表用户手册(下)

看潮成长日程表用户手册&#xff08;下&#xff09; 四、基础设置1、系统用户设置2、成长学员设置3、自然期间定义4、时间表版本设置5、学员期间设置6、时间表时段设置7、年度假日维护 五、参数设置1、颜色参数设置2、逻辑参数设置3、数值参数设置4、字符参数设置5、列表输入项…

【二分查找 位运算】3145. 大数组元素的乘积

本文涉及知识点 二分查找算法合集 位运算、状态压缩、枚举子集汇总 LeetCode3145. 大数组元素的乘积 一个整数 x 的 强数组 指的是满足和为 x 的二的幂的最短有序数组。比方说&#xff0c;11 的强数组为 [1, 2, 8] 。 我们将每一个正整数 i &#xff08;即1&#xff0c;2&am…

c语言从入门到函数速成(完结篇)

哈喽&#xff0c;小伙伴们大家好呀&#xff0c;本篇文章是这个系列的完结篇&#xff0c;希望大家看完后能有所收获哦 首先能看到这里的同学&#xff0c;一定也是自觉性比较强的了&#xff0c;我会在文章末尾给大家发点小福利 那么&#xff0c;我们先来通过数学中的函数来引入一…

医学中脑机接口技术的未来

医学中脑机接口技术的未来 李升伟 编译 对非侵入性脑机接口&#xff08;而不是植入物&#xff09;日益增长的兴趣可能会提高患者的易使用性&#xff0c;但分辨率需要提高。 图片来源&#xff1a;Denis Pobytov / DigitalVision Vectors / Getty 全球范围内正在展开一场争夺利用…

云服务器购买之后到部署项目的流程

1.通过账号密码登录百度智能云控制台; 2.进入对应的服务器‘云服务器BBC’ 找到’实例‘即找到对应的服务器列表; 此时通过本地电脑 1.cmd命令提示符 PING 服务器公网地址不通&#xff1b; 2.通过本地电脑进行远程桌面连接不通 原因&#xff1a;没有关联安全组&#xff0c;或者…

测试基础02:软件开发流程及模型、敏捷开发

1、软件开发流程 包括&#xff1a;项目开发目的分析与确定、需求分析、设计、编程、软件测试、软件交付、验收和维护。 2、软件开发模型 2.1 定义 软件开发模型(Software Development Model)是软件开发全过程的框架&#xff0c;规定了软件开发过程中各项活动的基本步骤、任务…

InteractiveGraph图谱中vue项目中如何使用

InteractiveGraph图谱中vue项目中如何使用 一、下载js和css和字体二、vue2.0项目中引用三、grap组件 一、下载js和css和字体 //在这里面找 https://github.com/grapheco/InteractiveGraph/blob/master/dist/examples/example1.html二、vue2.0项目中引用 //main.js中全局引入$ …

驱动开发中引入私有数据的原因

系列文章目录 驱动开发中引入私有数据的原因 驱动开发中引入私有数据的原因 系列文章目录驱动开发中引入私有数据的原因 驱动开发中引入私有数据的原因 驱动开发中引入私有数据&#xff08;Private Data&#xff09;概念主要是为了解决以下几个关键问题&#xff1a; 1.多设备支…

Docker环境安装并使用Elasticsearch

1、拉取es docker pull elasticsearch:7.10.12、查看镜像 docker images3、启动es docker run -d --name esearch -p 9200:9200 -p 9300:9300 elasticsearch:7.10.14、如果启动ES时出现一下问题 Unable to find image docker.elastic.co/elasticsearch/elasticsearch:7.10.…

从Python代码到pip包:打包Python项目

大家好&#xff0c;在软件开发的世界中&#xff0c;共享和重用代码是至关重要的。Python社区为我们提供了丰富的资源&#xff0c;使得我们能够轻松地与他人分享我们的工作&#xff0c;并从他人的工作中受益。将代码打包成pip包&#xff08;Python包管理器&#xff09;是一种常见…

SpringCloudAlibaba:6.2RocketMQ的普通消息的使用

简介 普通消息也叫并发消息&#xff0c;是发送效率最高&#xff0c;使用最多的一种 依赖 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSch…