目录
一、类加载过程
1.1 加载(Loading)
1.2 链接(Linking)
(1) 验证(Verify)
(2) 准备(Prepare)
(3) 解析(Resolve)
1.3 初始化(Initialization)
二、类加载器
2.1 引导类加载器(Bootstrap ClassLoader)
2.2 扩展类加载器(Extension ClassLoader)
2.3 系统类加载器(Application ClassLoader)
三、双亲委派机制
四、类加载时机
当程序主动使用某个类时,如果该类还未被加载到内存中,则JVM会通过加载、连接、初始化3个步骤来对该类进行初始化。如果没有意外,JVM将会连续完成3个步骤,所以有时也把这个3个步骤统称为类加载或类初始化。
加载指的是将类的class文件读入到内存,并为之创建一个java.lang.Class对象,也就是说,当程序中使用任何类时,系统都会为之建立一个java.lang.Class对象。类加载器子系统负责从文件系统或者网络中加载Class文件,class文件在文件开头有特定的文件标识(CA FE BA BY),让类加载器来识别。
加载的过程:
验证阶段用于检验被加载的类是否有正确的内部结构,确保Class文件字节流中包含信息符合当前虚拟机的要求,保证被加载类的正确性,不会危害虚拟机自身安全。
主要包括四种验证:
文件格式验证、元数据验证、字节码验证、符号引用验证
类准备阶段负责为类的静态变量分配内存,并设置默认初始值,即零值。
这里不包括用final修饰的static,因为final在编译的时候就会分配了,准备阶段会显式初始化。
也不会为实例变量初始化,类变量会分配在方法区中,而实例变量是会随着对象一起分配到Java堆中。
将常量池中的二进制数据中的符号引用替换成直接引用的过程。
事实上,解析操作往往会伴随着JVM在执行完初始化之后再执行。
初始化阶段就是执行类构造器方法
变量的赋值过程:在linking中的prepare操作中会先将类变量默认赋值为0,Initialization操作会按照语句在源文件中的顺序重新为其赋值。
注:
1. 若该类具有父类,JVM会保证子类的
2. 虚拟机必须保证一个类的
由C/C++语言编写,不是ClassLoader子类。它用来加载 Java 的核心类,是用原生代码来实现的,并不继承自 java.lang.ClassLoader,负责加载JAVA_HOME中jre/lib/rt.jar里所有的class。由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,所以不允许直接通过引用进行操作。
引导类加载器可以加载扩展类和应用程序类加载器,并为他们指定父类加载器。
出于安全考虑,Bootstrap启动类加载器只加载包名为java、javax、sun等开头的类
由Java语言编写,派生于ClassLoader类,父类加载器是null。它从java.ext.dirs系统属性所指定的目录中加载类库,或从JDK安装目录的jre/lib/ext子目录(扩展目录)下加载类库。如果用户创建的JAR文件放在此目录下,也会自动由扩展类加载器加载。
派生于ClassLoader类,父类加载器是扩展类加载器。负责加载环境变量classPath或系统属性java.class.path 指定路径下的类库。
该类加载 是程序中默认的类加载器,一般来说java应用的类都是由他来完成加载。通过ClassLoader.getSystemClassLoader()方法可以获取到该类加载器。
获取类加载器的几种方法:
方式一:获取当前类的ClassLoader
class.getClassLoader();
方式二:获取当前线程上下文的ClassLoader
Thread.currentThead().getContextClassLoader();
方式三:获取系统的ClassLoader
ClassLoader.getSystemClassLoader();
方式四:获取调用者的ClassLoader
DriverManager.getCallerClassLoader();
Java虚拟机对class文件采用的是按需加载的方式,也就是说当需要使用该类时才会将他的class文件加载到内存生成class文件。而且加载某个类的class文件时,Java虚拟机采用的双亲委派模式,即把请求交由父类处理,它是一种任务委派模式。
双亲委派机制的优点:
Java程序对类的使用方式分为:主动使用和被动使用。区别在于会不会导致类的初始化(Initialization)。
主动使用:
除了以上七种情况,其他使用Java类的方式都是被看作对类的被动使用,都不会导致类的初始化
补充:
在JVM中表示两个class对象是否为同一个类存在两个必要条件:
JVM必须知道一个类型是由启动类加载器加载的还是用户类加载器加载的。如果一个类型是由用户类加载器加载的,那么JVM会将这个类加载器的一个引用作为类型信息的一部分保存在方法区中。当解析一个类型到另一个类型的引用的时候,JVM需要保证这两个类的类加载器是相同的。