JVM学习 类加载子系统

JVM

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

Java代码执行流程

JVM学习 类加载子系统

简图

JVM学习 类加载子系统

详细图

JVM学习 类加载子系统

1、类加载子系统

JVM学习 类加载子系统

类加载器子系统的作用

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

类的加载过程图

JVM学习 类加载子系统

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插件集成了的)

JVM学习 类加载子系统

JVM学习 类加载子系统

1.4、类加载器的分类

  1. JVM支持两种类型的类加载器,分别是引导类加载器(Bootstrap ClassLoader)和自定义类加载器(User-Defined ClassLoader)。
  2. 从概念上来讲,自定义类加载器一般指的是程序中由开发人员自定义的一类加载器,但是Java虚拟机规范却没有这么定义,而是将所有派生于抽象类ClassLoader的类加载器都划分为自定义类加载器
    JVM学习 类加载子系统

测试:

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. 如果父类加载器可以完成类加载任务,则成功返回;倘若父类加载器不能完成加载,子类加载器才会尝试去加载,这就是双亲委派机制

JVM学习 类加载子系统

测试:

这里在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对类的使用,就是被动使用

Original: https://www.cnblogs.com/shuisanya/p/16686379.html
Author: 水三丫
Title: JVM学习 类加载子系统

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/581434/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • Docker安装及配置镜像加速

    Docker 支持 Mac Windows Linux 的三种安装 1、系统要求 官网提示如果要安装 Docker Engine, 需要一个CentOS 7 以及以上的稳定版本。 …

    Linux 2023年5月27日
    0105
  • haproxy服务部署

    haproxy haproxy 一、haproyx是什么 二、负载均衡类型 三、部署haproxy 1.源码部署haproxy 2.Haproxy搭建http负载均衡 一、hapr…

    Linux 2023年6月6日
    0108
  • Shell添加任务计划

    添加任务计划,每30分钟自动执行 /data1/scripts/chk_sds.sh mkdir /data1/scripts echo -e "if [ \ps -C …

    Linux 2023年5月28日
    077
  • 用Windows Terminal替代cmd

    HKEY_CLASSES_ROOT\batfile\shell\open\command,默认项内容 把 "C:\Users\<user>\AppData\L…

    Linux 2023年6月13日
    0103
  • Mysql数据库 ALTER 基本操作

    背景: ALTER作为DDL语言之一,工作中经常遇到,这里我们简单介绍一下常见的几种使用场景 新建两个测试表offices 和 employess CREATE TABLE off…

    Linux 2023年6月6日
    0109
  • 剑指offer计划28(搜索与回溯算法困难)—java

    1.1、题目1 剑指 Offer 37. 序列化二叉树 1.2、解法 这题给我笑死了,我看到题解有个解法,我愿称之为神。 public class Codec { private …

    Linux 2023年6月11日
    0100
  • shell升级

    对/sbin/nologin的理解 系统账号的shell使用 /sbin/nologin ,此时无法登陆系统,即使给了密码也不行。 所谓”无法登陆”指的仅是…

    Linux 2023年5月28日
    0103
  • UE4编辑器使用PS4/NS PRO手柄

    在Steam里,点击添加非Steam游戏,把Unreal Engine添加进去,进大屏幕模式,设置手柄配置为强制开启即可! 网上看到各种教程,都太复杂了………

    Linux 2023年6月6日
    0118
  • vscode搜索所有文件夹中所有文件的方法

    最近在看opencv相关的内容,看到画图这一部分时,提示我 这些代码都来自OpenCV代码的sample文件夹。 按照他的提示,我打开了相应的文件夹,却发现,so many 文件 …

    Linux 2023年6月14日
    0264
  • JuiceFS v1.0 正式发布,首个面向生产环境的 LTS 版本

    今天,JuiceFS v1.0 发布了 🎉 经过了 18 个月的持续迭代和大量生产环境的广泛验证,此版本将成为第一个被长期维护的稳定版(LTS)。同时,该版本提供完整的向前兼容,所…

    Linux 2023年6月14日
    094
  • Android recovery支持adb shell

    Android recovery支持adb shell 近期开发过程注意到recovery不支持adb shell。为了便于调试方便,决定添加此功能。 刚開始我们採用的是user版…

    Linux 2023年5月28日
    098
  • 利用 C# 给 Windows 资源管理器注册右键菜单(Windows Shell)(一):入门

    前言 关于 SharpShell SharpShell makes it easy to create Windows Shell Extensions using the .NE…

    Linux 2023年5月28日
    085
  • Redis6 源码调式

    Redis6 源码调式 安装Cygwin 1、下载安装Cygwin 去Cygwin的官方网站http://www.cygwin.com/ window 64位请选择 setup-x…

    Linux 2023年5月28日
    0123
  • SSH_远程终端

    SSH 远程服务 目的 Windwos 和 Linux 的终端控制系统or传送传送文件, 当然 Linux和Linux 以及 Windwos 和 Windwos 之间的通信都是OK…

    Linux 2023年6月7日
    085
  • 001 研发同学必学哪些 Linux 命令?

    身为研发同学,Linux 是绕不过去的一个小山包,不是说要掌握的十分精通,在程序员界里做个极客,也不是说要抢了 Devops 同学的饭碗,但至少要做到摆脱对 Linux 命令认知的…

    Linux 2023年5月27日
    085
  • 设计模式——命令模式

    命令模式定义 将一个请求封装成一个对象,从而让你使用不同的请求把客户端参数化,对请求排队或者记录请求日志,可以提供命令的撤销和回复的功能。 Receive接收者角色 该角色就是干活…

    Linux 2023年6月7日
    0108
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球