- 什么是类加载?
- 类就是 class 文件,类加载就是 JVM 把 class 文件加载到内存中的过程,这就叫类加载。
- 类加载的具体过程?
- 类加载的过程可以分为三部分:
- 加载:首先把 class 文件加载到 JVM 的内存中,并且为之创建一个 java.lang.Class 的对象;
- 链接:主要是把类的二进制合并到 JRE 中,其主要分为三个步骤:
- 验证:主要验证被加载的类是否有正确的内部结构,其中包括:文件格式验证、元数据验证码、字节码验证码以及符号引用验证。
- 文件格式验证:主要验证字节流是否符合 Class 文件格式规范,并且能被当前的虚拟机加载处理;
- 元数据验证:对字节码描述的信息进行语义分析,分析其是否符合 java 语言语法的规范;
- 字节码验证:对数据流和控制进行分析,确定语义是否合法以及符合逻辑;
- 符号引用验证:主要确定访问的类型以及涉及到的引用是否一定会被访问到,不会出现类无法访问的情况;
- 准备:准备阶段主要是为类的静态变量分配内存,并设置默认的初始值;
- 解析:将类的二进制符号引用替换成直接引用。这里解释一下符号引用和直接引用,符号引用就是以一组符号来描述所引用的目标,它可以是任何字面形式的字面量,只要不冲突就行;直接引用可以理解为指向目标的指针、偏移量或者直接定位的句柄。
- 验证:主要验证被加载的类是否有正确的内部结构,其中包括:文件格式验证、元数据验证码、字节码验证码以及符号引用验证。
- 初始化:最通俗的讲法就是为类的静态变量赋予正确的初始值。这里需要注意的是,这个和第二阶段的准备是不相互矛盾的,第二阶段的准备的初始值是指数据类型的初始值,例如:static int n=10; 第二阶段的准备是把 n 赋值为int类型的默认值 0 ,而到了这一步才会把 10 赋值给 n。
- 类加载的过程可以分为三部分:
- 有多少中类加载器?
- 根加载器(Bootstrap class loader):主要加载 java 中的核心类库,负责加载 $JAVA_HOME 中 jre/lib 里所有的jar,也可以通过设置 -Xbootclasspath 参数指定加载的jar文件,是由 C++ 实现的,并不是 ClassLoder 的子类;
- 扩展加载器(Extensions class loader):它负责加载 JRE 的扩展目录,在 $JAVA_HOME 中 lib/ext 目录下的类库,也可以通过系统变量 -Djava.ext.dir 来指定加载的类库,是由 java 语言实现,父加载器为 null。
- 系统加载器:(System class loader):也被称为类加载器,它主要是在 jvm 启动时加载系统类路径 java -classpath 或者 -D java.class.path 指定的类库,其父类加载器就是 扩展加载器(Extensions class loader) ,另外其也是自定义的类加载器的父加载器。
- 类加器加载 Class 文件的模式,双亲委托模式
- 当加载 class 文件的时候,类加载器会先委托为父类加载器进行加载,如果父类加载器还有父类,就依次递归继续往上委托,直到没有父类为止。如果父类可以加载成功,那么直接返回该类的 class 对象;如果父类无法加载此 class 文件,那么便自己进行加载。
- 双亲委托模式最大的优势就是可以防止类重复加载,因为如果父类已经加载过来,就会直接返回已经加载过的对象。其次也是比较安全的,可以防止 API 库被随意篡改。
- 双亲委托模式大致示意图如下: