Java类的加载和加载器

1,083 阅读5分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

本系列专栏:JVM专栏

前言

我们知道Java语言的类型可以分为2大类:基本类型和引用类型,而在上一篇文章中,我们已经介绍了Java的基本类型,他们是由Java虚拟机预先定义好的。

对于引用类型,Java将其分为4种:类、接口、数组和泛型参数,由于泛型参数在编译过程中会被擦除,以此Java虚拟机实际上只有前3种。而且数组是由Java虚拟机直接生成的,类和接口则有对应的字节流。

何为字节流,也就是我们平时把.java文件通过编译器生成的.class文件,那JVM是如何加载Java类的呢 我们来仔细分析一下,后面文章为了方便,类和接口都用类来统称。

正文

从class文件到内存中的类,按照先后顺序需要经过加载、链接和初始化三大步骤,后面的使用和卸载暂时不说明,整个流程如下图:

image.png

加载

所谓加载,简而言之就是将Java类的字节码文件加载到机器内存中,并且在内存中构建出Java类的原型--类模板对象。

啥是类模板对象呢,其实也就是Java类在JVM内存中的一个快照,JVM将从字节码文件中解析的常量池、类字段、类方法等信息存储到模板中,这样JVM在运行期间便可以通过该模板获取Java类的任意信息,这也是反射可以实现的基础。

所以加载时主要做下面3件事:

  • 通过类的全名,获取类的二进制数据流;

  • 解析类的二进制数据流为方法区内的数据结构(Java类模型);

  • 创建java.lang.Class类的实例,表示该类型,作为方法区这个类的各种数据的访问入口。

类模型与Class实例的位置

加载的类在JVM中创建相应的类结构,类结构会存储在方法区(JDK1.8之前:永久代;JDK1.8之后:元空间)

类将.class文件加载到方法区后,会在堆中创建一个java.lang.Class对象,用来封装位于该方法区的数据结构,该Class对象是在加载类的过程中创建的,而且只能由JVM创建,每个类都对应有一个Class类型的对象。

所以加载完如下图所示:

image.png

外部可以通过访问代表Order类的Class对象来获取Order的类数据结构。

类加载器的分类

前面说了字节码文件是由类加载器来加载到JVM内存中的,但是这里的类加载器又分为几种,各有不同的用处,我们来看一下。

  • 启动类加载器(bootstrap clas loader),这个是加载器的祖师爷,由C++实现,没有对应的Java对象。它的作用是加载最为基础、最为重要的类,比如存放在JRE的lib目录下的jar包,同时它还有个重要作用是加载其他加载器器。

  • java.lang.ClassLoader,除了启动类加载器外,其他的类都是这个类的子类,都是有对应的Java对象,而这些Java加载器需要由其他加载器,比如启动类加载器来加载到JVM中,方能执行类加载。

  • 扩展类加载器(extension class loader),其父加载器便是启动类加载器,负责加载相对次要、但又通用的类,比如存放在JRE的lib/ext目录下的jar包中的类。

  • 应用类加载器(application class loader),其父类便是扩展类加载器,它负责加载引用程序路径下的类,默认情况下,应用程序中包含的类便由引用类加载器加载。

  • 自定义类加载器,对于一些特殊的需求,我们可以使用自定义的类加载器来完成类的加载。

所以上面几种加载器总结如下:

image.png

双亲委托机制

当某个特定的类加载器接受到加载的请求时,会将加载任务委托给自己的父类,直到最高级父类启动类加载器,如果父类能加载就加载,不能加载则返回到子类进行加载,如果都不能加载则报错 ClassNotFoundException。

大致如下图:

image.png

双亲委托机制是为了保证Java核心库的类型安全,这种机制保证不会出现用户自己能定义java.lang.Object类等的情况。例如用户定义了java.lang.String,那么加载这个类时最高级别父类会首先加载,发现核心类中也有该类,那就加载了核心类库,而自定义的不会被加载。

自定义类加载器

其实自定义类加载器,也就是继承ClassLoader,过程还是比较简单的,大致流程如下:

1、首先检查请求的类型是否已经被这个类加载器加载到名门空间中去了,如果已经加载则直接返回,否则转入第二步;

2、委派给父类加载器,如果父类加载器能够完成,则返回父类加载器加载的Class实例,否则转入第三步;

3、调用本类加载器的findClass方法获取对应的字节码,如果获取到,则调用defineClass导入类型到方法区。

总结

本篇文章有限,只介绍了Java的类加载机制,下篇文章会继续介绍类加载其他2个步骤:链接和初始化,还有就是Android的类加载也有一点不一样,后续继续介绍。