Java类加载知识01-Java的类加载过程
- 前言
- 类加载
- 加载
前言
一个Java文件从编码完成到最终执行,一般主要包括两个过程
- 编译
- 运行
编译,即把我们写好的java文件,通过javac命令编译成字节码,也就是我们常说的.class文件。
运行,则是把编译生成的.class文件交给Java虚拟机(JVM)执行。
而我们所说的类加载过程即是指JVM虚拟机把.class文件中类信息加载进内存,并进行解析生成对应的class对象的过程。
举个通俗点的例子来说,JVM在执行某段代码时,遇到了class A, 然而此时内存中并没有class A的相关信息,于是JVM就会到相应的class文件中去寻找class A的类信息,并加载进内存中,这就是我们所说的类加载过程。
由此可见,JVM不是一开始就把所有的类都加载进内存中,而是只有第一次遇到某个需要运行的类时才会加载,且只加载一次。
类加载
类加载的过程主要分为三个部分:
- 加载
- 链接
- 初始化
而链接又可以细分为三个小部分:
- 验证
- 准备
- 解析
加载
简单来说,加载指的是把class字节码文件从各个来源通过类加载器装载入内存中。
这里有两个重点:
- 字节码来源。一般的加载来源包括从本地路径下编译生成的.class文件,从jar包中的.class文件,从远程网络,以及动态代理实时编译
- 类加载器。一般包括启动类加载器,扩展类加载器,应用类加载器,以及用户的自定义类加载器。
注:为什么会有自定义类加载器?
- 一方面是由于java代码很容易被反编译,如果需要对自己的代码加密的话,可以对编译后的代码进行加密,然后再通过实现自己的自定义类加载器进行解密,最后再加载。
- 另一方面也有可能从非标准的来源加载代码,比如从网络来源,那就需要自己实现一个类加载器,从指定源进行加载。
验证
主要是为了保证加载进来的字节流符合虚拟机规范,不会造成安全错误。
包括对于文件格式的验证,比如常量中是否有不被支持的常量?文件中是否有不规范的或者附加的其他信息?
对于元数据的验证,比如该类是否继承了被final修饰的类?类中的字段,方法是否与父类冲突?是否出现了不合理的重载?
对于字节码的验证,保证程序语义的合理性,比如要保证类型转换的合理性。
对于符号引用的验证,比如校验符号引用中通过全限定名是否能够找到对应的类?校验符号引用中的访问性(private,public等)是否可被当前类访问?
解析
将常量池内的符号引用替换为直接引用的过程。
两个重点:
符号引用。即一个字符串,但是这个字符串给出了一些能够唯一性识别一个方法,一个变量,一个类的相关信息。
直接引用。可以理解为一个内存地址,或者一个偏移量。比如类方法,类变量的直接引用是指向方法区的指针;而实例方法,实例变量的直接引用则是从实例的头指针开始算起到这个实例变量位置的偏移量
举个例子来说,现在调用方法hello(),这个方法的地址是1234567,那么hello就是符号引用,1234567就是直接引用。
在解析阶段,虚拟机会把所有的类名,方法名,字段名这些符号引用替换为具体的内存地址或偏移量,也就是直接引用。
初始化
初始化阶段是执行类构造器方法的过程。方法是由编译器自动收集类中的类变
量的赋值操作和静态语句块中的语句合并而成的。虚拟机会保证子方法执行之前,父类
的方法已经执行完毕,如果一个类中没有对静态变量赋值也没有静态语句块,那么编译
器可以不为这个类生成()方法。
注意以下几种情况不会执行类初始化:
- 通过子类引用父类的静态字段,只会触发父类的初始化,而不会触发子类的初始化。
- 定义对象数组,不会触发该类的初始化。
- 常量在编译期间会存入调用类的常量池中,本质上并没有直接引用定义常量的类,不会触
发定义常量所在的类。 - 通过类名获取 Class 对象,不会触发类的初始化。
- 通过 Class.forName 加载指定类时,如果指定参数 initialize 为 false 时,也不会触发类初
始化,其实这个参数是告诉虚拟机,是否要对类进行初始化。 - 通过 ClassLoader 默认的 loadClass 方法,也不会触发初始化动作。
总结
类加载过程只是一个类生命周期的一部分,在其前,有编译的过程,只有对源代码编译之后,才能获得能够被虚拟机加载的字节码文件;在其后还有具体的类使用过程,当使用完成之后,还会在方法区垃圾回收的过程中进行卸载。如果想要了解Java类整个生命周期的话,可以自行上网查阅相关资料,这里不再多做赘述。
在面试过程中类加载过程虽然是一个老生常谈的问题,但是往往从这个问题还可以衍生出很多其他重要的知识点,已经罗列在下文中,如果大家感兴趣的话,可以自行学习,小编也会在之后的文章中,对其中的一些问题进行解答和总结。
相关扩展知识点:
Java虚拟机的基本机构?
- 什么是类加载器?
- 简单谈一下类加载的双亲委托机制?
- 普通Java类的类加载过程和Tomcat的类加载过程是否一样?区别在哪?
- 简单谈一下Java堆的垃圾回收机制?