草庐IT

JVM学习 类加载子系统

水三丫 2023-04-17 原文

JVM

哔哩哔哩 尚硅谷视频 宋红康老师


Java代码执行流程

简图

详细图


1、类加载子系统

类加载器子系统的作用

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

类的加载过程图


1.1、加载阶段

加载

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

1.2、链接阶段

验证(Verify)

  • 目的在于确保Class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全
  • 主要包括四种验证,文件格式验证,元数据验证,字节码验证,符号引用验证

准备(Prepare)

  • 为类变量分配内存并且设置该类变量的默认初始值,即零值
  • 这里不包括用final修饰的static,因为final在编译的时候就分配了,准备阶段会显示初始化
  • 这里不会为实例变量分配初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中

解析(Resolve)

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

1.3、初始化阶段

初始化

  • 初始化阶段就是执行类构造器方法()方法的过程
  • 此方法不需定义,是javac编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来
  • 构造器方法中指令按语句在源文件中出现的顺序执行
  • ()不同于类的构造器。(关联:构造器是虚拟机视角下的())
  • 若该类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕
  • 虚拟机必须保证一个类的()方法在多线程下被同步加锁

安装 jclasslib is a bytecode viewer 来查看class字节码文件(Ider插件集成了的)


1.4、类加载器的分类

  1. JVM支持两种类型的类加载器,分别是引导类加载器(Bootstrap ClassLoader)自定义类加载器(User-Defined ClassLoader)

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

  3. 无论类加载器的类型如何划分,在程序中我们最常见的类加载器始终只有3个:

    这里的四者之间的关系是包含关系,不是下层下层关系,也不是子父类关系的继承关系

测试:

public class ClassLoaderTest {
    public static void main(String[] args) {
        //获取系统类加载器
        ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
        System.out.println(systemClassLoader);

        //获取其上层,扩展类加载器
        ClassLoader extClassLoader = systemClassLoader.getParent();
        System.out.println(extClassLoader);

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

        //用户自定义类的加载器是谁
        ClassLoader classLoader = ClassLoaderTest.class.getClassLoader();
        System.out.println(classLoader);

        //String这个类是谁加载的:引导类加载器
        ClassLoader stringClassLoader = String.class.getClassLoader();
        System.out.println(stringClassLoader);
    }

    /*
     * 结果:
     * sun.misc.Launcher$AppClassLoader@18b4aac2
     * sun.misc.Launcher$ExtClassLoader@1b6d3586
     * null
     * sun.misc.Launcher$AppClassLoader@18b4aac2
     * null
     */
}

Java的核心类库都是引导类加载器加载的

虚拟机自带的加载器

  • 启动类加载器(引导类加载器:Bootstrap ClassLoader)
    • 这个类加载使用C/C++语言实现的,嵌套在JVM内部
    • 它用来加载Java的核心库(JAVA_HOME/jre/lib/rt.jar、resources.jar或sun.boot.class.path路径下的内容),用于提供JVM自身需要的类)
    • 并继承自java.lang.ClassLoader,没有父加载器
    • 加载扩展类和应用程序类加载器,并指定为他们的父类加载器
    • 出于安全的考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
  • 扩展类加载器(Extension ClassLoader)
    • Java语言编写,由sun.misc.Launcher$ExtClassLoader实现
    • 派生于ClassLoader类
    • 父类加载器为启动类加载器
    • 从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载。
  • 应用程序类加载器(系统类加载器,AppClassLoader)
    • Java语言编写,由sun.misc.Launcher$AppClassLoader实现
    • 派生于ClassLoader类
    • 父类加载器为扩展类加载器
    • 它负责加载环境变量classpath或系统属性 java.class.path 指定路径下的类库
    • 该类加载是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载的
    • 通过classLoader#getSystemClassLoader()方法可以获取该类加载器

测试:

package com.mhy.day01;

import sun.misc.Launcher;
import java.net.URL;

public class ClassLoaderTest01 {
    public static void main(String[] args) {
        //引导类加载器加载哪些路径下的文件
        System.out.println("引导类加载器加载的路径:");
        URL[] urLs = Launcher.getBootstrapClassPath().getURLs();
        for (URL urL : urLs) {
            System.out.println(urL);
        }
        //扩展类加载器加载哪些路径下的文件
        System.out.println("扩展类加载器加载的路径:");
        String property = System.getProperty("java.ext.dirs");
        for(String p : property.split(";")){
            System.out.println(p);
        }

        /*结果:
            引导类加载器加载的路径:
            file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/resources.jar
            file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/rt.jar
            file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/sunrsasign.jar
            file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/jsse.jar
            file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/jce.jar
            file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/charsets.jar
            file:/F:/Program%20Files/JavaIDEA/jdk/jre/lib/jfr.jar
            file:/F:/Program%20Files/JavaIDEA/jdk/jre/classes
            扩展类加载器加载的路径:
            F:\Program Files\JavaIDEA\jdk\jre\lib\ext
            C:\WINDOWS\Sun\Java\lib\ext
         */
    }
}

1.5、双亲委派机制

工作原理

  1. 如果一个类加载器收到一个类加载的请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行
  2. 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,最终的请求回到达启动类加载器
  3. 如果父类加载器可以完成类加载任务,则成功返回;倘若父类加载器不能完成加载,子类加载器才会尝试去加载,这就是双亲委派机制

测试:

这里在src文件下创建一个java.lang.String和自带的String同路径

package java.lang;

public class String {
    static {
        System.out.println("这是我们自己建立的String");
    }
    
    //如果在这个里面执行main方法
    /*
    错误: 在类 java.lang.String 中找不到 main 方法, 请将 main 方法定义为:
    public static void main(String[] args)
    否则 JavaFX 应用程序类必须扩展javafx.application.Application
     */
    public static void main(String[] args) {
        System.out.println("xxx");
    }
}

再在测试类中进行测试,看使用的String到底来自哪个String

package com.mhy.day01;

public class ClassLoaderTest02 {
    public static void main(String[] args) {
        String xx = new String();
        System.out.println("执行了该程序");
    }

    /*结果:
     * 执行了该程序
     */
}

1.6、类的主动使用和被动使用

  • 主动使用主要分为7种:

    • 创建类的实例

    • 访问某个类或接口的静态变量,或者对该静态变量赋值

    • 调用该类的静态方法

    • 反射(比如Class.forName("路径")))

    • 初始化一个类的子类

    • Java虚拟机启动时被表明为启动类的类

    • JDK7提供的动态语言的支持:

      java.lang.invoke,MethodHandle实例的解析结果

      REF_getStatic、REF_putStatic、REF_invokeStatic句柄对应的类没有初始化,则初始化

  • 除了以上7种外,其他的Java对类的使用,就是被动使用

有关JVM学习 类加载子系统的更多相关文章

  1. ruby - 如何在续集中重新加载表模式? - 2

    鉴于我有以下迁移:Sequel.migrationdoupdoalter_table:usersdoadd_column:is_admin,:default=>falseend#SequelrunsaDESCRIBEtablestatement,whenthemodelisloaded.#Atthispoint,itdoesnotknowthatusershaveais_adminflag.#Soitfails.@user=User.find(:email=>"admin@fancy-startup.example")@user.is_admin=true@user.save!ende

  2. ruby - RuntimeError(自动加载常量 Apps 多线程时检测到循环依赖 - 2

    我收到这个错误:RuntimeError(自动加载常量Apps时检测到循环依赖当我使用多线程时。下面是我的代码。为什么会这样?我尝试多线程的原因是因为我正在编写一个HTML抓取应用程序。对Nokogiri::HTML(open())的调用是一个同步阻塞调用,需要1秒才能返回,我有100,000多个页面要访问,所以我试图运行多个线程来解决这个问题。有更好的方法吗?classToolsController0)app.website=array.join(',')putsapp.websiteelseapp.website="NONE"endapp.saveapps=Apps.order("

  3. ruby-on-rails - 使用 config.threadsafe 时从 lib/加载模块/类的正确方法是什么!选项? - 2

    我一直致力于让我们的Rails2.3.8应用程序在JRuby下正确运行。一切正常,直到我启用config.threadsafe!以实现JRuby提供的并发性。这导致lib/中的模块和类不再自动加载。使用config.threadsafe!启用:$rubyscript/runner-eproduction'pSim::Sim200Provisioner'/Users/amchale/.rvm/gems/jruby-1.5.1@web-services/gems/activesupport-2.3.8/lib/active_support/dependencies.rb:105:in`co

  4. 电脑0x0000001A蓝屏错误怎么U盘重装系统教学 - 2

      电脑0x0000001A蓝屏错误怎么U盘重装系统教学分享。有用户电脑开机之后遇到了系统蓝屏的情况。系统蓝屏问题很多时候都是系统bug,只有通过重装系统来进行解决。那么蓝屏问题如何通过U盘重装新系统来解决呢?来看看以下的详细操作方法教学吧。  准备工作:  1、U盘一个(尽量使用8G以上的U盘)。  2、一台正常联网可使用的电脑。  3、ghost或ISO系统镜像文件(Win10系统下载_Win10专业版_windows10正式版下载-系统之家)。  4、在本页面下载U盘启动盘制作工具:系统之家U盘启动工具。  U盘启动盘制作步骤:  注意:制作期间,U盘会被格式化,因此U盘中的重要文件请注

  5. 【鸿蒙应用开发系列】- 获取系统设备信息以及版本API兼容调用方式 - 2

    在应用开发中,有时候我们需要获取系统的设备信息,用于数据上报和行为分析。那在鸿蒙系统中,我们应该怎么去获取设备的系统信息呢,比如说获取手机的系统版本号、手机的制造商、手机型号等数据。1、获取方式这里分为两种情况,一种是设备信息的获取,一种是系统信息的获取。1.1、获取设备信息获取设备信息,鸿蒙的SDK包为我们提供了DeviceInfo类,通过该类的一些静态方法,可以获取设备信息,DeviceInfo类的包路径为:ohos.system.DeviceInfo.具体的方法如下:ModifierandTypeMethodDescriptionstatic StringgetAbiList​()Obt

  6. ruby-on-rails - 从应用程序中自定义文件夹内的命名空间自动加载 - 2

    我们目前正在为ROR3.2开发自定义cms引擎。在这个过程中,我们希望成为我们的rails应用程序中的一等公民的几个类类型起源,这意味着它们应该驻留在应用程序的app文件夹下,它是插件。目前我们有以下类型:数据源数据类型查看我在app文件夹下创建了多个目录来保存这些:应用/数据源应用/数据类型应用/View更多类型将随之而来,我有点担心应用程序文件夹被这么多目录污染。因此,我想将它们移动到一个子目录/模块中,该子目录/模块包含cms定义的所有类型。所有类都应位于MyCms命名空间内,目录布局应如下所示:应用程序/my_cms/data_source应用程序/my_cms/data_ty

  7. LC滤波器设计学习笔记(一)滤波电路入门 - 2

    目录前言滤波电路科普主要分类实际情况单位的概念常用评价参数函数型滤波器简单分析滤波电路构成低通滤波器RC低通滤波器RL低通滤波器高通滤波器RC高通滤波器RL高通滤波器部分摘自《LC滤波器设计与制作》,侵权删。前言最近需要学习放大电路和滤波电路,但是由于只在之前做音乐频谱分析仪的时候简单了解过一点点运放,所以也是相当从零开始学习了。滤波电路科普主要分类滤波器:主要是从不同频率的成分中提取出特定频率的信号。有源滤波器:由RC元件与运算放大器组成的滤波器。可滤除某一次或多次谐波,最普通易于采用的无源滤波器结构是将电感与电容串联,可对主要次谐波(3、5、7)构成低阻抗旁路。无源滤波器:无源滤波器,又称

  8. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  9. 深度学习部署:Windows安装pycocotools报错解决方法 - 2

    深度学习部署:Windows安装pycocotools报错解决方法1.pycocotools库的简介2.pycocotools安装的坑3.解决办法更多Ai资讯:公主号AiCharm本系列是作者在跑一些深度学习实例时,遇到的各种各样的问题及解决办法,希望能够帮助到大家。ERROR:Commanderroredoutwithexitstatus1:'D:\Anaconda3\python.exe'-u-c'importsys,setuptools,tokenize;sys.argv[0]='"'"'C:\\Users\\46653\\AppData\\Local\\Temp\\pip-instal

  10. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

随机推荐