• jvm虚拟机之类加载器子系统


    子系统结构图

    在这里插入图片描述

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

    类加载器角色

    在这里插入图片描述

    Class file存储于硬盘上,相当于一个模板,用于将这个模板在执行时加载到JVM中来根据此文件可实例化很多实例。class file加载到JVM中,被称为DNA元数据模板,放在方法区。在.class文件->JVM->最终成为元数据模板,过程中需要进行数据的运输,而ClassLoader就扮演成此角色。

    类加载过程

    在这里插入图片描述

    • 验证(Verify)
      • 目的在于确保class文件的字节流中包含信息符合当前虚拟机要求,保证被加载类的正确性,不会危害虚拟机自身安全。
      • 主要包含四种验证,文件格式验证、元数据验证、字节码验证、符合引用验证。
    • 准备(Prepare)
      • 为类变量分配内存并且设置该类变量的默认初始值,则零值
      • 这里不包含用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显示初始化;
      • 这里不会为实例变量分配初始化,类变量会分配在方法区,而实例变量是会随着对象一起分配给Java堆中。
    • 解析(Resolve)
      • 将常量池内的符号引用转换为直接引用的过程。
      • 事实上,解析操作往往会伴随着jvm在执行完初始化后在执行
      • 符号引用就是一组符号来描述所引用的目标
      • 解析动作主要针对类或接口、字段、类方法、接口方法、方法类型等。对应常量池中的CONSTANT_CLASS_info、CONSTANT_Fieldref_info、CONSTANT_Methodref_info等。
    • 初始化
      • 初始化阶段就是执行类构造器方法()的过程
      • 此方法不需定义,是Java编译器自动收集类中的所有类变量的赋值动作和静态代码块中的语句合并而来
      • 构造器方法中指令按语句在源文件中出现的顺序执行
      • ()不同于类的构造器。(关联:构造器是虚拟机视角下的())
      • 若该类具有父类,JVM会保证子类的()执行前,父类的()已经执行完毕
      • 虚拟机必须保证一个类的()方法在多线程下被同步加锁。

    虚拟机自带的加载器

    • 启动类加载器(引导类加载器,BootStrap classLoader)
      1. 这个类加载使用c/c++语言实现的,嵌套在JVM内部
      2. 它用来加载Java的核心库,用于提供jvm自身需要的类
      3. 并不继承自java.lang.classLoader,没有父加载器
      4. 加载扩展类和应用程序类加载器,并指定为他们的父类加载器。
      5. 处于安全考虑,BootStrap启动类加载器值加载包名为java、javax、sun等开头的类
    • 扩展类加载器(Extension classLoader)
    sun.misc.Launcher$ExtClassLoader@7ea987ac 
    
    • 1
    1. java语言编写
    2. 派生于ClassLoader类
    3. 父类加载器为启动类加载器
    4. 从Java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录下加载类库,如果用户创建的JAR放在此目录下,也会自动由扩展类加载器加载。
    • 应用程序类加载器(系统类加载器,AppClassLoader)
     sun.misc.Launcher$AppClassLoader
    
    • 1
    1. java语言编写
    2. 派生于ClassLoader类
    3. 父类加载器为扩展类加载器
    4. 它负责加载环境变量class path或系统属性Java.class.path指定路径下的类库
    5. 该类加载是程序中默认的类加载器,一般来说,Java应用的类都是由它来完成加载
    6. 通过ClassLoader#getSystemClassLoader()方法可以获取到该类加载器。
      案例实例代码块:
     System.out.println("********启动类加载器********");
        //获取BootstrapClassLoader能够加载的api的路径
        URL[] urls=sun.misc.Launcher.getBootstrapClassPath().getURLs();
        for (URL element : urls) {
            System.out.println(element.toExternalForm());
        }
        //从上面的路径中随意选择一个类,来看看他的类加载器是什么:引导类加载器
        ClassLoader classLoader = Provider.class.getClassLoader();
        System.out.println(classLoader);
    
        System.out.println("--------------------------------------------------------------------------");
    
        System.out.println("********扩展类加载器********");
        //
        String extDirs=System.getProperty("java.ext.dirs");
        for (String path : extDirs.split(";")) {
            System.out.println(path);
        }
        //从上面的路径中随意选择一个类,来看看他的类加载器是什么:sun.misc.Launcher$ExtClassLoader@7ea987ac   扩展类加载器
        ClassLoader classLoader1 = CurveDB.class.getClassLoader();
        System.out.println(classLoader1);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    除了上诉三种加载器还存在用户自定义加载器,还存在自定义加载类

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

    • 隔离加载类
    • 修改类加载的方式
    • 扩展加载源
    • 防止源码泄露

    关于ClassLoader

    ClassLoader类,它是一个抽象类,其后所有的类加载器都继承自ClassLoader(不包括启动加载器)
    在这里插入图片描述

    双亲委派机制

    java 虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将它的class文件加载到内存生成class对象。而且加载某个类的class文件时,Java虚拟机采用的是双亲委派机制,即把请求交由父类处理,它是一种任务委派模式。
    机制工作原理

    • 如果一个类加载器收到了类加载请求,它并不会自己先去加载,而是把这个请求委托给父类的加载器去执行。
    • 如果父类加载器还存在其父类加载器,则进一步向上委托,依次递归,请求最终将到达顶层的启动类加载器;
    • 如果父类加载器可以完成类加载任务,就成功返回,倘若父类加载器无法完成此加载任务,子加载器才会尝试自己去加载,这就是双亲委派模式。
      在这里插入图片描述

    注意访问顺序:首先访问先问问引导类加载器,如果不管就是拓展类加载器,如果还不管就是系统类加载器,而自定义类加载器是最后出现的。

    俗话说:小孩拿到一个苹果,出于礼貌首先会问妈你吃吗?(拓展类加载器),孩儿妈处于礼貌就是问孩儿奶奶说妈你吃吗?(引导类加载器)。接着孩儿奶说不吃那么就需要判断孩儿妈吃不吃,如果孩儿妈也不吃那么就说孩儿自己吃(系统类加载器)特别注意苹果不可以分哟
    在这里插入图片描述

    优势

    • 避免类的重复加载
    • 保护程序安全,防止核心API被随意篡改
      • 自定义类:Java.lang.String
      • 自定义类:java.lang.ShkStart
    package java.lang;  //注意这是设定的系统目录
    
    /**
     * Author:梦醒
     * Date: 2022/7/16 17:23
     * Dream:
     * 信言不美,美言不信。
     * 善者不辩,辩者不善。
     * 知者不博,博者不知。
     * Function:
     */
    
    public class ShkStart {
        public static void main(String[] args) {
            System.out.println("hello");
        }
    }
    报错:java.lang.SecurityException: Prohibited package name: java.lang
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    沙箱安全机制

    自定义String类,但是在加载自定义String类的时候会率先使用引导类加载器加载,而引导类加载器在加载的过程中会先加载jdk自带的文件(rt.jar包中java\lang\String.class),报错信息说没有main方法,就是因为加载的是rt.jar包中的String类。这样可以保证对Java核心源代码的保护,这就是沙箱安全机制

  • 相关阅读:
    一级消防工程师证书价值下降,前景茫然?
    深入了解网络流量清洗--使用免费的雷池社区版进行防护
    如何获取nod把子元素去掉,只需要n的父元素
    利用Spring Boot后端与Vue前端技术构建现代化电商平台
    力扣(LeetCode)481. 神奇字符串(C++)
    从实践的角度谈谈主成分分析
    【Codeforces Round #811 (Div. 3)】【题目解析+AK代码】
    [附源码]JAVA毕业设计高校医务管理系统(系统+LW)
    几种反序列化漏洞
    【初识JavaScript-运算符总合】
  • 原文地址:https://blog.csdn.net/wh1236666/article/details/126170438