文章目录
参考
《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》 - 周志明
CyC2018/CS-Notes
类的生命周期
- 一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历
加载
、 验证 、 准备、 解析、初始化
、 使用和卸载 七 个 阶 段。
1. 加载
- 完成3件事情
- 读取类的
二进制字节流
。(可以从任意位置获取,如直接Class文件、JAR包、网络、实时计算、数据库、加密文件等) - 将二进制字节流
格式化
为方法区的运行时数据结构
- 生成
Class对象
2. 验证
- 进行文件格式验证、元数据验证、字节 码验证和符号引用验证,
确保JVM不会受恶意代码攻击
3. 准备
- 将类中的static变量
赋0值
- 如果同时被
final
修饰,则直接赋为给定常量值
。
4. 解析
- 将常量池内的符号引用替换为直接引用
5. 初始化
- 编译器在编译期间将static变量和static代码块组织成
<clinit >()
方法 - JVM在初始化阶段执行
<clinit >()
方法,完成2件事情
- 初始化static变量
- 执行static代码块
- static变量和static代码块是由编译器从上向下收集的,因此不能向前引用变量
public class Test {
static {
i = 0; // 给变量复制可以正常编译通过
System.out.print(i); // 这句编译器会提示“非法向前引用”
}
static int i = 1;
}
- JVM需要保证
<clinit >()
方法在多线程情况下的同步,同一个ClassLoader下,一个Class只会被初始化1次
类什么时候开始初始化?
- JVM不会一次性初始化所有的类,只有在
需要用的时候
才会初始化,具体来说就是发生主动引用
时 - 开始初始化之前,加载、 验证 、 准备、 解析阶段要在之前完成,但完成的时机由JVM决定。
1. 主动引用
有且只有
以下6种情况
- new对象、读静态成员变量(final常量除外)、调用静态方法
- reflect反射调用
- 如果父类没有初始化,需要先初始化父类
- JVM启动时,默认初始化用户指定的主类(包含main方法)
- MethodHandle相关
- 如果实现的接口有默认方法,需要先初始化接口
2. 被动引用
- 除了主动引用都是被动引用,不会触发类初始化,其核心是
虽然有引用,但需要考虑该引用是否需要对类进行初始化
- 3个例子
- 子类引用父类静态变量
class SuperClass{
static int value = 123;
}
class SubClass extends SuperClass{
}
public static void main(String[] args){
System.out.println(SubClass.value);
}
//这里虽然存在对SubClass的引用,但访问的是父类的静态变量,那么只需要初始化SuperClass即可,不需要初始化SubClass
- 通过数组定义引用类
SuperClass[] sca = new SuperClass[10];
//这里只是存在定义而已,不需要对SuperClass进行初始化操作
- StaticFinal常量
class ConstClass{
public static final String A = "a";
}
public static void main(String[] args){
System.out.println(ConstClass.A);
}
//这里的常量A直接存入常量池中了,这个引用也不需要初始化ConstClass
类加载器
-
类加载的第一个任务是获取类的
二进制字节流
,Java给了很大的灵活性,字节流的获取放到了JVM的外部去实现,具体就需要用户使用ClassLoader类
去加载所需的类。 -
JVM中的任意一个类,都必须由加载它的
类加载器
和这个类本身
一起共同确定其唯一。判断两个类相等(instanceof,equals等)需要类一致,类加载器一致。
1. 分类
- 在JVM的角度,ClassLoader有两种
- 一种是
启动类加载器
(Bootstrap ClassLoader),使用C++实现,是JVM的一部分 - 一种是
其他类加载器
,在JVm外部,使用Java继承ClassLoader类实现
- 更进一步细分及作用
启动类加载器
加载JAVA_HOME\lib目录下的类,约定在Java中该ClassLoader的实例为null扩展类加载器
加载JAVA_HOME\lib\ext目录下的类应用程序类加载器
加载用户ClassPath上的类,一般是程序中默认的ClassLoader自定义类加载器
自定义从其他位置加载类
2. Parents Delegation M odel
- 一般称为
双亲委派模型
,这个翻译感觉优点不准备,我认为应该翻译为父类代理加载模型
- 流程:
如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加 载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的 加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请 求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
- 好处
基础类得到统一,Java 类随着它的类加载器一起具有一种带有优先级
的层次关系