第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虚拟机》

浙公网安备 33010602011771号