类加载器深入解析
类加载
- 在JAVA代码中,类型(指类的本身比如class、interface、枚举,不代表对象比如new出来的实例)的加载(其中一种为:将已经存在的类class文件从磁盘上加载到内存中)、连接(将类与类关系处理好)与初始化过程(类的静态变量赋值)都是在程序运行期间(RUNTIME )完成的。
- 提供更大的灵活性,增加更多的可能性。
类的加载、连接与初始化
- 加载: 查找并加载类的二进制数据
- 连接
- 验证:确保被加载的类的正确性
- 准备: 为类的静态变量分配内存,并将其初始化为默认值
- 解析:把类中的符号引用转换为直接引用
注: 调用方法 hello() ,这个方法的地址是0xaab,那么hello就是符号引用,0xaab 就是直接引用。在解析阶段,虚拟机会把所有的类名、方法名、字段名这些符号引用替换为具体的内存地址或偏移量。
- 初始化:为类的静态变量赋予正确的初始值
类的使用与卸载
- 使用 : 类的实例进行调用
- 卸载 : 类的卸载会在内存中卸载相关的内存空间
类的加载、连接与初始化
- Java程序对类的使用方式分为两种
- 主动使用(七种)
(1) 创建类的实例
(2) 访问某个类或接口的静态变量,或者对该静态变量赋值
(3) 调用类的静态方法
(4) 反射
(5) 初始化类的子类
(6) Java虚拟机启动时被标明为启动类的类
(7)JDK1.7 开始提供的动态语言支持:
java.lang.invok.MethodHandle实例的解析结果REWF_getStatic,REF——putStatic
注:除了上述七种情况,其他使用Java类的方式都被看作为被动使用,都不会导致类的初始化
- 被动使用
- 所有的Java虚拟机实现必须在每个类或接口被Java程序“首次主动使用”时才初始化他们
类的加载
类的加载指的是将类的.class文件中的二进制数据读入到内存中,将其放在运行时数据区内,然后在内存中创建一个java.lang.Class对象(规范并未说明Class对象位于哪里,HotSpot虚拟机将其放在了方法区)用来封装类在方法区内的数据结构
加载.class文件方式
- 从本地系统中直接加载
- 通过网络下载.class文件
- 从zip、jar等归档文件中加载.calss文件
- 从专有数据库中提取.class文件
- 将Java源文件动态编译为.class文件 (动态代理 AOP)
范例一
class MyTest {
public static void main(String[] args) {
System.out.println(MyChild.str);
}
}
class MyParent {
public static String str = "This is MyParent's str";
static {
System.out.println("This is MyParet.");
}
}
class MyChild extends MyParent {
public static String str1 = "This is MyChild's str1";
static {
System.out.println("This is MyChild.");
}
}
执行结果
This is MyParet.
This is MyParent's str
(1)对于静态变量来说,只有直接定义了该变量的类才会被初始化
(2)上述代码只有主动使用了父类的静态变量并没有主动使用子类MyChild的静态变量;
(3)上述讲到Java虚拟机只有对于主动使用接口和类的时候才会初始化他们;
(4)所以虽然主动调用了MyChild.str 但是由于调用的是父类MyParent的静态变量,所以只会初始化父类MyParent。
范例二
class MyTest {
public static void main(String[] args) {
System.out.println(MyChild.str1);
}
}
class MyParent {
public static String str = "This is MyParent's str";
static {
System.out.println("This is MyParet.");
}
}
class MyChild extends MyParent {
public static String str1 = "This is MyChild's str1";
static {
System.out.println("This is MyChild.");
}
}
执行结果
This is MyParet.
This is MyChild.
This is MyChild's str1
(1)由于调用的是子类的str1,所以子类会被初始化
(2)上述主动使用第五种方式:初始化一个类的子类,该类也会被初始化,所以MyParent由于子类MyChild被初始化了会先初始化。
(3)当一个类在初始化时,要求其父类全部都已经初始化完成。
本篇文章主要是整理学张龙老师的深入理解JVM课程的学习笔记
学习如逆水行舟,不进则退。