Java 通过引入字节码和 JVM 机制,提供了强大的跨平台能力,理解 Java 的类加载机制是深入 Java 开发的必要条件。
Java程序运行时,必须经过编译和运行两个步骤。首先将后缀名为.java的源文件进行编译,最终生成后缀名为.class的字节码文件。然后JVM虚拟机启动时,会初始化好类加载器(ClassLoader)。通过ClassLoader,JVM将编译好的字节码文件加载到内存(类加载)。最后由JVM对加载到内存的java类进行解释执行,显示结果。
以我们常见的Test.java为例,具体流程如下图所示:
类加载过程主要分为三个步骤:加载、链接、初始化,而其中链接过程又分为三个步骤:验证、准备、解析,加上卸载、使用两个步骤统称为为类的生命周期。
简单来说,加载指的是把class字节码文件从各个来源通过类加载器装载入内存中。
由于没有具体指明需要在哪里获取class文件,导致字节码来源途径非常丰富:
从压缩包中读取,如jar、war
从网络中获取,如Web Applet
动态生成,如动态代理、CGLIB
由其他文件生成,如JSP
从数据库读取
从加密文件中读取
将静态储存解析成运行时数据,存放在方法区
在堆区生成该类的Class对象,作为方法区这个类的各种数据的访问入口。
验证阶段主要是为了为了确保Class文件的字节流中包含的信息符合虚拟机要求,并且不会危害虚拟机。
而验证主要分为以下四类:
文件格式验证
元数据验证
字节码验证
符号引用验证
准备阶段会为类的静态变量分配内存、赋初值
数据类型
零值
int
0
long
0L
short
(short)0
char
‘’
byte
(byte)0
boolean
false
float
0.0f
double
0.0d
reference
null
需要注意的有以下几点:
解析阶段会将符号引用替换为直接引用,该过程也被称为静态链接。
以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可
可以直接指向目标的指针、相对偏移量或者是一个能间接定位到目标的句柄。而直接引用必须引用的目标已经在内存中存在。
即符号引用替换为直接引用的阶段,该阶段会把一些静态方法(符号引用,比如main()方法)替换为指向数据所存内存的指针或句柄等(直接引用)。
初始化阶段是执行类构造器 () 方法的过程。这一步主要的目的是:根据程序员程序编码制定的主观计划去初始化类变量和其他资源。
需要在主动引用时,才会执行初始化
除了主动引用外,还有以下三种情况被称为被动引用,不会触发初始化
触发类加载过程的时机主要分为隐式加载和显示加载两种情况
类加载完成后主要包括类信息以及类Class对象,其中类信息保存在方法区中,类Class对象保存在堆区。
类信息主要包含运行时常量池、类型信息、字段信息、方法信息、类加载器的引用、对应class实例的引用等信息。
类加载器的引用:这个类到类加载器实例的引用
对应class实例的引用:类加载器在加载类信息放到方法区中后,会创建一个对应的Class 类型的对象实例放到堆(Heap)中, 作为开发人员访问方法区中类定义的入口和切入点。