类加载过程

Java文件编译执行的过程

​ 要想理解类加载器的话,务必要先清楚对于一个Java文件,它从编译到执行的整个过程。
在这里插入图片描述

  • 类加载器:用于装载字节码文件(.class文件)
  • 运行时数据区:用于分配存储空间
  • 执行引擎:执行字节码文件或本地方法
  • 垃圾回收器:用于对JVM中的垃圾内容进行回收

类加载的过程

1、类的加载
2、验证:确保类符合JVM规范,保证安全性
3、准备:为类变量分配内存和初始化值
4、解析:JVM常量池中的符号引用(常量名)直接替换成直接引用(地址)
5、初始化:JVM对类进行初始化

类加载器介绍

​ 前面提到过,JVM只会运行二进制文件,而类加载器(ClassLoader)的主要作用就是将字节码文件加载到JVM中,从而让Java程序能够启动起来。现有的类加载器基本上都是java.lang.ClassLoader的子类,该类的只要职责就是用于将指定的类找到或生成对应的字节码文件,同时类加载器还会负责加载程序所需要的资源。该类中主要方法如下所示:

方法解释
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.lang.ClassLoader类的子类对象或者C++代码编写的Bootstrap ClassLoader,它们的作用是加载字节码到JVM内存,得到Class类的对象。

​ 类加载器根据各自加载范围的不同,划分为四种类加载器:

  • 启动类加载器(BootStrap ClassLoader):

    该类并不继承ClassLoader类,其是由C++编写实现。用于加载JAVA_HOME/jre/lib目录下的类库。

  • 扩展类加载器(ExtClassLoader):

    该类是ClassLoader的子类,主要加载JAVA_HOME/jre/lib/ext目录中的类库。

  • 应用类加载器(AppClassLoader):

    该类是ClassLoader的子类,主要用于加载classPath下的类,也就是加载开发者自己编写的Java类。

  • 自定义类加载器:

    开发者自定义类继承ClassLoader,实现自定义类加载规则。

上述三种类加载器的层次结构如下如下:
在这里插入图片描述
类加载器的体系并不是“继承”体系,而是委派体系,类加载器首先会到自己的parent中查找类或者资源,如果找不到才会到自己本地查找。类加载器的委托行为动机是为了避免相同的类被加载多次。

类加载模型

​ 在JVM中,对于类加载模型提供了三种,分别为全盘加载、双亲委派、缓存机制。

  • 全盘加载:

​ 即当一个类加载器负责加载某个Class时,该Class所依赖和引用的其他Class也将由该类加载器负责载入,除非显示指定使用另外一个类加载器来载入。

  • 双亲委派:

​ 即先让父类加载器试图加载该Class,只有在父类加载器无法加载该类时才尝试从自己的类路径中加载该类。简单来说就是,某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才自己去加载。

  • 缓存机制:

​ 会保证所有加载过的Class都会被缓存,当程序中需要使用某个Class时,类加载器先从缓存区中搜寻该Class,只有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存入缓冲区中。从而可以理解为什么修改了Class后,必须重新启动JVM,程序所做的修改才会生效的原因。

双亲委派解析

​ 上述已经大致介绍了双亲委派,对于双亲委派具体的工作流程,如下图所示:
在这里插入图片描述
类加载器的体系并不是“继承”体系,而是委派体系,类加载器首先会到自己的parent中查找类或者资源,如果找不到才会到自己本地查找。类加载器的委托行为动机是为了避免相同的类被加载多次。

2.4.3 JVM为什么采用双亲委派机制

1)通过双亲委派机制可以避免某一个类被重复加载,当父类已经加载后则无需重复加载,保证唯一性。

2)为了安全,保证类库API不会被修改

在工程中新建java.lang包,接着在该包下新建String类,并定义main函数

public class String {

    public static void main(String[] args) {

        System.out.println("demo");
    }
}

​ 此时执行main函数,会出现异常,在类 java.lang.String 中找不到 main 方法
在这里插入图片描述
出现该信息是因为由双亲委派的机制,java.lang.String的在启动类加载器(Bootstrap classLoader)得到加载,因为在核心jre库中有其相同名字的类文件,但该类中并没有main方法。这样就能防止恶意篡改核心API库。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值