一:背景
类加载机制是在我们的真个java的运行阶段中的其中一个阶段。
二:什么是快乐星球(类加载机制)
我们编写的 Java 文件都是以.java 为后缀的文件,编译器会将我们编写的.java 的文件编译成.class 文件,简单来说类加载机制就是jvm从文件系统将一系列的 class 文件z转化为二进制流加载 JVM 内存中并生成一个该类的Class对象,为后续程序运行提供资源的动作。
三:类加载的流程
1:整体的流程图
2:各个阶段的解析
(1):加载
a:加载的类的字节码文件以及二进制文件的来源
通过一个类的完整路径查找此类字节码文件(class 文件即二进制文件)。将二进制文件的静态存储结构转化为方法区的运行时数据结构,并利用二进制流文件创建一个Class对象,存储在 Java 堆中用于对方法区的数据结构引用的入口;
class 文件的来源:有一点需要注意的是类加载机制不仅可以从文件系统读取 class 文件,也可以通过网络获取,其他 jar 包或者其他程序生成,如 JSP 应用。
b:类加载器
• 类加载器:讲到类加载不得不讲到类加载的顺序和类加载器。Java 中大概有四种类加载器,分别是:启动类加载器(Bootstrap ClassLoader),扩展类加载器(Extension ClassLoader),系统类加载器(System ClassLoader),自定义类加载器(Custom ClassLoader),依次属于继承关系(注意这里的继承不是 Java 类里面的 extends)
启动类加载器(Bootstrap ClassLoader):主要负责加载存放在Java_Home/jre/lib下,或被-Xbootclasspath参数指定的路径下的,并且能被虚拟机识别的类库(如rt.jar,所有的java.*开头的类均被Bootstrap ClassLoader加载),启动类加载器是无法被Java程序直接引用的。
• 扩展类加载器(Extension ClassLoader):主要负责加载器由sun.misc.Launcher$ExtClassLoader实现,它负责加载Java_Home/jre/lib/ext目录中,或者由java.ext.dirs系统变量指定的路径中的所有类库(如javax.*开头的类),开发者可以直接使用扩展类加载器。
• 系统类加载器(System ClassLoader):主要负责加载器由sun.misc.Launcher$AppClassLoader来实现,它负责加载用户类路径(ClassPath)所指定的类,开发者可以直接使用该类加载器,如果应用程序中没有自定义过自己的类加载器,一般情况下这个就是程序中默认的类加载器。
• 自定义类加载器(Custom ClassLoader:自己开发的类加载器
c:双亲委派
如果一个类加载器需要加载类,那么首先它会把这个类加载请求委派给父类加载器去完成,如果父类还有父类则接着委托,每一层都是如此。
一直递归到顶层,当父加载器无法完成这个请求时,子类才会尝试去加载。
(2):验证
验证的过程只要是保证 class 文件的安全性和正确性,确保加载了该 class 文件不会导致 JVM 出现任何异常,不会危害JVM 的自身安全。验证包括对文件格式的验证,元数据和字节码的验证。
(3):准备
准备阶段是为类变量进行内存分配和初始化零值的过程。注意这时候分配的是类变量的内存,这些内存会在方法区中分配。此时不会分配实例变量的内存,因为实例变量是在实例化对象时一起创建在Java 堆中的。而且此时类变量是赋值为零值,即 int 类型的初值为 0,引用类型初值为 null,而不是代码中显示赋值的数值。
(4):解析
将常量池的符号引用转化成直接引用。符号引用可以理解为只是个替代的标签,比如你此时要做一个计划,暂时还没有人选,你设定了个 A 去做这个事。然后等计划真的要落地的时候肯定要找到确定的人选,到时候就是小明去做一件事。
解析就是把 A(符号引用) 替换成小明(直接引用)。符号引用就是一个字面量,没有什么实质性的意义,只是一个代表。直接引用指的是一个真实引用,在内存中可以通过这个引用查找到目标。
(5):初始化
初始化的阶段是类加载的最后一步,这个阶段主要是执行 java 代码,进行相关初始化的动作;这时候就执行一些静态代码块,为静态变量赋值,这里的赋值才是代码里面的赋值,准备阶段只是设置初始值占个坑。