Java类加载器和加载机制详解

一、原理:
JVM将class文件字节码文件加载到内存中,并将这些静态数据转换成方法区中的运行时数据结构,在堆(并不一定在堆中,HotSpot在方法区中)中生成一个代表这个类的java.lang.class对象,作为方法区类数据的访问入口。

二、类加载过程:
JVM类加载机制分为五部分:加载,验证,准备,解析,初始化。其中,加载,检验,准备,初始化,和卸载这五个阶段的顺序是固定的,而解析则未必。为了支持动态绑定,解析这个过程可以发生在初始化阶段之后。
在这里插入图片描述
加载:
主要完成三件事:

  1. 通过类的全限定名来获取定义此类的二进制字节流
  2. 将这个类字节流代表的静态存储结构转为方法区的运行时数据结构
  3. 在堆中生成一个代表此类的java.lang.Class对象,作为访问方法区这些数据结构的入口
    这个过程主要是类加载器完成。

校验:
此阶段主要确保class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机的自身安全。

  • 文件格式的验证:基于字节流验证。
  • 元数据验证:基于方法区的存储结构验证。
  • 字节码验证:基于方法区的存储结构验证。
  • 符号引用验证:基于方法区的存储结构验证。

准备:
为类变量分配内存,并将其初始化为默认值。(此时为默认值,在初始化的时候才会给变量赋值)即在方法区中分配这些变量所使用的内存空间,例如:

public static int value = 123;

此时在准备阶段过后的初始值为0而不是123;将value赋值为123的putstatic指令是程序被编译后,存放于类构造器方法中,特例:

public static final int value = 123;

此时value的值在准备阶段过后就被赋值为123。

解析:
把类型中的符号引用转换为直接引用。

  • 符号引用与虚拟机实现的布局无关,引用的目标并不一定要已经加载到内存中。各种虚拟机实现的内存布局可以各不相同,但是它们能接受的符号引用必须是一致的,因为符号引用的字面量形式明确定义在Java虚拟机规范的Class文件格式中。
  • 直接引用可以是指向目标的指针,相对偏移量或是一个能间接定位到目标的句柄。如果有了直接引用,那引用的目标必定已经在内存中存在。

主要有四种:

  1. 类或接口的解析
  2. 字段解析
  3. 类方法解析
  4. 接口方法解析

初始化:
初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证方法执行之前,父类的方法已经执行完毕。如果一个类中没有对静态变量赋值也没有静态语句块,那么编译器可以不为这个类生成()方法。

三、类加载时机

  1. 创建类的实例,也就是new一个对象
  2. 访问某个类或接口的静态变量,或者对该静态变量赋值
  3. 调用类的静态方法
  4. 反射(Class.forName(“com.lyj.load”))
  5. 初始化一个类的子类(会首先初始化子类的父类)
  6. JVM启动时标明的启动类,即文件名和类名相同的那个类

特殊情形:
对于一个final类型的静态变量,如果该变量的值在编译时就确定下来,那么这个变量相当于“宏变量”。Java编译器会在编译时直接把这个变量出现的地方替换成他的值,因此即使程序使用该静态变量,也不会导致该类的初始化。
反之,如果该final类型的静态变量的值不能在编译时就确定下来,则必须等到运行时才可以确定该变量的值,如果通过该类来访问它的静态变量,则会导致该类被初始化。

被动引用,不会触及类加载:

a.引用子类使用其父类的static非final字段(直接定义这个字段的类),只会初始化父类,不会初始化子类。子类是否加载、验证(类加载第1,2阶段),取决于虚拟机具体实现,无规定

b.通过数组定义引用类,不会触发该类初始化。这里触发了一个虚拟机自动生成的代表数组的类的初始化,里面有数组相关属性,比如public的length字段(数组长度)

c.一个类引用另一个类的public static final字段,不会初始化另一个类。该字段引用在编译时进入了前一个类(引用类)的常量池中,编译后就与后一个类完全没关系了。

d.接口初始化:不要求初始化其父类接口,这与类初始化不同。只有使用到父接口比如引用其常量时才会初始化父类接口

四、类加载器
类加载器负责加载所有的类,其为所有被载入内存中的类生成一个java.lang.Class实例对象。一旦一个类被加载如JVM中,同一个类就不会被再次载入了。正如一个对象有一个唯一的标识一样,一个载入JVM的类也有一个唯一的标识。在Java中,一个类用其全限定类名(包括包名和类名)作为标识;但在JVM中,一个类用其全限定类名和其类加载器作为其唯一标识。 例如,如果在pg的包中有一个名为Person的类,被类加载器ClassLoader的实例kl负责加载,则该Person类对应的Class对象在JVM中表示为(Person.pg.kl)。这意味着两个类加载器加载的同名类:(Person.pg.kl)和(Person.pg.kl2)是不同的、它们所加载的类也是完全不同、互不兼容的。

JVM预定义有三种类加载器,当一个JVM启动时,Java就开始使用如下三种类加载器:

1)根类加载器(bootstrap class loader):它用来加载Java的核心类,是用原生代码来实现的,并不继承自java.lang.ClassLoader。(负责加载$JAVA_HOME中jre/lib/rt.jar里所有的class,由C++实现,不是ClassLoader子类) 由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。

2)扩展类加载器(extensions class loader):它负责加载JRE的扩展目录,lib/ext或者由java.ext.dirs系统属性指定的目录中的JAR包的类。由Java语言实现,父类加载器为null。

3)系统类加载器(System class loader):也被称为应用类加载器,它负责在JVM启动时加载来自Java命令的-classpath选项,java.class.path系统属性,或者classpath将变量所指的JAR包和类路径。
程序可以通过ClassLoader的静态方法getSystemClassLoader()来获取系统类加载器。如果没有特别指定,则用户自定义的类加载器都是以此类作为父加载器,由Java语言实现,父类加载器为扩展类加载器。

类加载器加载Class的步骤:

  1. 检测此class是否载入过,即在缓冲区是否有此class,有就直接返回对应的class对象。
  2. 没有,如果没有父类加载器,则此类要么就是根类加载器,要么Parent是根类加载器,请求根类加载器区载入目标,载入成功直接返回class对象,载入失败,由当前类加载器去寻找class文件,找到就载入class,返回class对象。找不到抛出classNotFoundException异常。
  3. 如果有父类加载器,请求父类加载器去载入目标,载入成功直接返回class对象,载入失败,由当前类加载器去寻找class文件,找到就载入,返回class对象。找不到抛出classNotFoundException异常。

五、类加载机制
**1. 全盘负责:**当一个类加载负责加载某个class时,该class所依赖和引用其它class也将由该类加载器负责载入,除非显示使用另外一个类加载器来载入。

**2. 双亲委派:**某个特定的类加载器在接到加载类的请求时,首先将加载任务委托给父加载器,依次递归,请求最终将到达顶层的启动类加载器, 如果父加载器可以完成类加载任务,就成功返回;只有父加载器无法完成此加载任务时,才由自己去加载。

在这里插入图片描述
优势:

  • Java类随着它的类加载器一起具备了一种带有优先级的层次关系,通过这种层级关系可以避免类的重复加载,当父亲已经加载过此类时,就没有必要子类加载器也加载一遍。
  • 从安全方面,Java核心API中定义类型不会被随意替换,假设通过网络传递一个名为java.lang.Integer的类,通过双亲委托模式传递到启动类加载器,而启动类加载器在核心的API发现这个名字的类,发现该类已被加载,直接返回已加载过的Integer.class,并不会重新加载网络传递过来的类,这样可以防止核心API库被随意篡改。

**3. 缓存机制:**保证所有加载过的class都会被缓存,当程序中需要使用某个class时,类加载器先从缓存区中搜索该class,只有当缓存区没有改class对象时,系统才会加载该类,存入缓冲区。这就是Java的class修改之后,必须要重新启动JVM,程序所作的修改才会生效的原因。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值