JVM类加载机制

发布时间 2023-12-21 11:31:29作者: 肖德子裕

JVM类加载机制

类加载机制概念

image-20221011112137522

概念说明:

1)虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验,解析和初始化,最终形成可以被虚拟机直接使用的Java类型。程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载(loading)、连接(linking)、初始化(initialization)3个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化。

2)Java中的所有类,都需要由类加载器装载到JVM中才能运行。类加载器本身也是一个类,而它的工作就是把Class文件从硬盘读取到内存中。在写程序的时候,我们几乎不需要关心类的加载,因为这些都是隐式装载的,除非我们有特殊的用法,像是反射,就需要显式的加载所需要的类。

类装载方式有两种:
隐式装载:程序在运行过程中当碰到通过new等方式生成对象时,隐式调用类装载器加载对应的类到JVM中。
显式装载:通过class.forname()等方法,显式加载需要的类。

3)Java类的加载是动态的,它并不会一次性将所有类全部加载后再运行,而是保证程序运行的基础类(像是基类)完全加载到JVM中,至于其他类,则在需要的时候才加载,这当然就是为了节省内存开销。

加载、连接过程、初始化

1)加载
加载指的是将类的Class文件读入到内存,并将这些静态数据转换成方法区中的运行时数据结构,并在堆中生成一个代表这个类的java.lang.Class对象,作为方法区类数据的访问入口,这个过程需要类加载器参与。Java类加载器由JVM提供,是所有程序运行的基础,JVM提供的这些类加载器通常被称为系统类加载器。除此之外,开发者可以通过继承ClassLoader基类来创建自己的类加载器。类加载器,可以从不同来源加载类的二进制数据,比如本地Class文件、Jar包Class文件、网络Class文件等。类加载的最终产物就是位于堆中的Class对象(注意不是目标类对象),该对象封装了类在方法区中的数据结构,并且向用户提供了访问方法区数据结构的接口,即Java反射的接口。

2)连接过程
当类被加载之后,系统为之生成一个对应的Class对象,接着将会进入连接阶段,连接阶段负责把类的二进制数据合并到JRE中(意思就是将Java类的二进制代码合并到JVM的运行状态之中)。

类连接又可分为如下3个阶段:
验证:确保加载的类信息符合JVM规范,没有安全方面的问题。主要验证是否符合Class文件格式规范,并且是否能被当前的虚拟机加载处理。
准备:正式为类变量(static变量)分配内存并设置类变量初始值的阶段,这些内存都将在方法区中进行分配。
解析:虚拟机常量池的符号引用替换为字节引用过程。

3)初始化
初始化阶段是执行类构造器<clinit>()方法的过程。类构造器<clinit>()方法是由编译器自动收藏类中的所有类变量的赋值动作和静态语句块(static块)中的语句合并产生,代码从上往下执行。当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化。虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确加锁和同步。

初始化的总结就是:初始化是为类的静态变量赋予正确的初始值。

类加载器类别

实现通过类的权限定名获取该类的二进制字节流的代码块叫做类加载器,主要有一下四种类加载器:
1)启动类加载器(Bootstrap ClassLoader)
用来加载java核心类库,无法被java程序直接引用。

2)扩展类加载器(extensions class loader)
它用来加载Java的扩展库。Java虚拟机的实现会提供一个扩展库目录,该类加载器在此目录里面查找并加载Java类。

3)系统类加载器(system class loader)
它根据Java应用的类路径(CLASSPATH)来加载Java类。一般来说,Java应用的类都是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取它。

4)用户自定义类加载器
通过继承java.lang.ClassLoader类的方式实现。

双亲委派模型

image-20221011114835716

概念说明:

如果一个类加载器收到了类加载的请求,它首先不会自己去加载这个类,而是把这个请求委派给父类加载器去完成,每一层的类加载器都是如此,这样所有的加载请求都会被传送到顶层的启动类加载器中,只有当父加载无法完成加载请求(它的搜索范围中没找到所需的类)时,子加载器才会尝试去加载类。