JVM:2、类加载子系统

在这里插入图片描述

1、类加载子系统的作用

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

2、类加载的过程

类的加载过程分为一下几步:
加载–>连接(验证、准备、解析)–>初始化

2、1 加载

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

2、2连接

2、2、1 验证(verify)

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

2、2、2 准备(Prepare)

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

2、2、3解析(Resolve)

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

2、3初始化

  • 初始化阶段就是执行类构造器方法< clinit> ()的过程
  • 此方法不需要定义,是java编译器自动收集类中所有类变量的复制动作和静态代码快中的语句的合并而来,如果没有类变量和静态代码块,是没有< clinit>()方法的
  • 构造器方法中指令按语句在源文件中出现的顺序执行,例如:
public class ClassInitTest {
    static {
        num = 20;
        System.out.println(num); // 编译不通过:非法的前向引用
    }
    private static int num = 10;
    public static void main(String[] args) {
        System.out.println(num); // 此处打印的值为 10
    }
}
  • < clinit >()不同于类的构造器(关联:构造器是虚拟机视角下的< init>())
  • 若此类具有父类,jvm会保证子啊子类的< clinit>()已经执行完毕
  • 虚拟机必须保证一个类的< clinit>()方法子啊多线程下被同步加锁

3、类加载器分类

JVM主要支持两种类型的classloader:

  • 引导类加载器(BootStraptClassLoader)
  • 自定义类型加载器(ExtensionClassLoader、SystemClassLoader、User-DefinedClassLoader)
    其中所有派生于ClassLoader的类加载器都统称为自定义类加载器
    在这里插入图片描述
    他们依次之间是父类关系,但是这种父类并不是继承关系。

3、1 BootStraptClassLoader

BootStraptClassLoader使用c/c++语言实现的,它用来加载java的核心类库(JAVA_HOME/jre/lib/rt.jar/resources.jar或sun.boot.class.path路径下的内容),用于提供jvm自身需要的类,出于安全考虑,BootStrapt启动类加载器值加载包名为java、javax、sun等开头的类

3、2 ExtClassLoader

ExtClassLoader由Java语言编写,它通过由sun.misc.Launcher$ExtClassLoader实现,派生于ClassLoader类,父类加载器为BoostStrapClassLoader。它用于从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK的安装目录的jre/lib/ext子目录(扩展目录)下加载类库。

3、3 AppClassLoader

AppClassLoader由java语言编写,同样有sun.misc.Launcher$AppClassLoader实现,派生于ClassLoader类,父类加载器为ExtClassLoader。他负责加载环境变量中的类加载器,一般来说,java应用的类都是由他完成加载,通过ClassLoader.getSystemClassLoader()可以获取到该类加载器。

3、4 特点

  • 层级结构:java中的类加载器之间具有层级结构,BootStraptClassLoader是所有类加载器的父亲
  • 双亲委派机制:当一个类加载某个类的时候,首先会检查他的父类加载器中是否已经对该类进行了加载,如果父类加载器已经加载过了,那么该类将会被直接使用,否则类加载器会请求加载该类。
  • 可见性限制:一个加载器可以查找父类加载器中的类,但是父类加载器不能查找子类加载器中的类。
  • 不允许卸载:类的加载器可以加载一个类但是不能执行卸载的操作,不过可以删除当前类的加载器,然后创建一个新的类加载器进行加载

4、ClassLoader的常用方法

除了BoostStrapClassLoader外,所有的类加载器都派生于ClassLoader。ClassLoader常用的方法如下:

  • getParent(): 返回该类加载器的超类加载器
  • loadClass(String name):加载名称为name的类,返回结果为java.lang.Class类的实例
  • findClass(String name):查找名称为name的类,返回结果为java.lang.Class类的实例
  • findLoadedClass(String name):查找名称为name的已经被加载过的类,返回结果为java.lang.Class类的实例
  • defineClass(String name,byte[] b,int off,int len):把字节数组b中的内容转换为一个Java类 ,返回结果为java.lang.Class类的实例
  • resolveClass(Class<?> c):连接指定的一个java类

5、获取ClassLoader的方式

  • 类对象.getClassLoader():获取当前类的ClassLoader
  • Thread.currentThread().getContextClassLoader():获取当前线程ContextClassLoader,默认和当前类的加载器一致
  • ClassLoader.getSystemClassLoader():获取系统默认的ClassLoader
  • DriverManager.getClasserClassLoader():获取调用者的ClassLoader
public class Car {

    String name;
    double price;

    public static void main(String[] args) throws ClassNotFoundException {
        ClassLoader classLoader = new Car().getClass().getClassLoader();
        System.out.println(classLoader); // jdk.internal.loader.ClassLoaders$AppClassLoader@3fee733d

        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        System.out.println(contextClassLoader);
        // jdk.internal.loader.ClassLoaders$AppClassLoader@3fee733d

        System.out.println(ClassLoader.getSystemClassLoader());
        // jdk.internal.loader.ClassLoaders$AppClassLoader@3fee733d
    }
}

6、 双亲委派机制

JVM对.Class文件采用的是按需加载的方式,当时用该类时,才将其字节码文件加载到内存中生成对应的Class文件,当加载某个类的字节码文件时,JVM采用的是双亲委派机制,他的核心思想为:

  • 自底向上检查类是否已经加载
  • 自顶向下尝试加载类

6、1 工作原理

  • 如果一个类的加载器收到了类加载请求,他并不回自己先去加载,而是把这个请求委派为父类加载器去执行
  • 如果父类加载器还有父类加载器,则进一步向上委派,依次递归,请求最终将达到顶层的BoostStrapClassLoader
  • 如果父类加载器可以完成类加载任务,则成功返回;如果无法加载,则子加载器自己加载
  • 如果所有的类加载器都无法进行加载,JVM会抛出ClassNotFoundException异常
    在这里插入图片描述通过双亲委派机制可以避免类的重复加载,同时保护程序的安全,防止核心API被随意篡改。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值