第23讲 请介绍类加载过程,什么是双亲委派模型?

Java通过引入字节码和JVM机制,提供了强大的跨平台能力,理解Java的类加载机制是深入Java开发的必要条件,也是个面试考查热点。

今天的问题是,请介绍类加载过程,什么是双亲委派模型?

典型回答

一般来说,我们把Java的类加载过程分为三个主要步骤:加载、链接、初始化,具体行为在Java虚拟规范里有非常详细的定义。

首先是加载阶段(Loading),它是Java将字节码数据从不同的数据源读取到JVM中,并映射为JVM认可的数据结构(Class对象),这里的数据源可能是各种各样的形态,如jar文件、class文件,甚至是网络数据源等;如果输入数据不是ClassFile的结构,则会抛出ClassFormatError。

加载阶段是用户参与的阶段,我们可以自定义类加载器,去实现自己的类加载过程。

第二阶段是链接(Linking),这是核心的步骤,简单说是把原始的类定义信息平滑的转入运行的过程。这里可以一共分为三个步骤:

  验证(Verification),这是虚拟机安全的重要保障,JVM需要核验字节码信息是否符合Java虚拟机规范的,否则就认为是VerifyError,这样就防止了恶意信息或者不符合规范的信息危害JVM运行,验证阶段可能会触发更多的class加载。

  准备(Preparation),创建类或接口中的静态变量,并初始化静态变量的初始值。

  解析(Resloution),在这一步会将常量池中的符号引用替换为直接引用。

最后阶段是初始化阶段(Initialization)。这一步真正去执行类初始化的代码逻辑,包括静态字段赋值的动作,以及执行类定义中的静态初始化块内的逻辑,编译器在编译阶段就会把这部分逻辑整理好,父类型的初始化逻辑优先于当前类型的逻辑。

 

什么是双亲委派模型? 

  简单来说,就是当类加载器(Class-Loader)试图加载某个类型的时候,除非父加载器找不到相应类型,否则尽量将这个任务代理给当前加载器的父加载器去做。使用委派模型的目的是避免重复加载Java类型。

 

考点分析

今天的问题是关于JVM类加载方面的基础问题,我在前面给出的回答参考了Java虚拟机规范中的主要条款。如果你在面试中回答这个问题,在这个基础上还可以举例说明。

我们来看一个经典的延伸问题,准备阶段谈到静态变量,那么对于常量和不同静态变量有什么区别?

需要明确的是,没有人能够精确的的理解和记忆所有信息,如果碰到这种问题,有直接答案当然最好;没有的话,就说说自己的思路。

我们定义下面这样的类型,分别提供了普通静态变量、静态常量,常量又考虑到原始类型和引用类型可能有区别。

public class CLPreparation{
    
 public static  int a = 100;
 public static final int INT_CONSTANT = 1000;   
 public static final Integer INTEGER_CONSTANT = Integer.ValueOf(1000);      
}

编译并反编译一下:

Javac CLPreparation.java
Javap -v CLPreparation.class

可以在字节码中看到这样的额外初始化逻辑:

 

 这能让我们更清楚,普通原始类型静态变量和引用类型(即使是常量),是需要额外调用putstatic等JVM指令的,这些是在初始化阶段执行,而不是准备阶段调用;而原始类型常量,则不需要这样的步骤。

 

推荐书籍

《深入理解Java虚拟机》

posted @ 2020-02-12 11:52  源问三生  阅读(200)  评论(0)    收藏  举报