【JVM】(3)类文件结构与加载机制

参考

《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》 - 周志明
CyC2018/CS-Notes

类的生命周期

  • 一个类型从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期将会经历加载、 验证 、 准备、 解析、初始化 、 使用和卸载 七 个 阶 段。
    在这里插入图片描述

1. 加载

  • 完成3件事情
  1. 读取类的二进制字节流 。(可以从任意位置获取,如直接Class文件、JAR包、网络、实时计算、数据库、加密文件等)
  2. 将二进制字节流格式化为方法区的运行时数据结构
  3. 生成Class对象

2. 验证

  • 进行文件格式验证、元数据验证、字节 码验证和符号引用验证,确保JVM不会受恶意代码攻击

3. 准备

  • 将类中的static变量赋0值
  • 如果同时被final修饰,则直接赋为给定常量值

4. 解析

  • 将常量池内的符号引用替换为直接引用

5. 初始化

  • 编译器在编译期间将static变量和static代码块组织成<clinit >()方法
  • JVM在初始化阶段执行<clinit >()方法,完成2件事情
  1. 初始化static变量
  2. 执行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种情况
  1. new对象、读静态成员变量(final常量除外)、调用静态方法
  2. reflect反射调用
  3. 如果父类没有初始化,需要先初始化父类
  4. JVM启动时,默认初始化用户指定的主类(包含main方法)
  5. MethodHandle相关
  6. 如果实现的接口有默认方法,需要先初始化接口

2. 被动引用

  • 除了主动引用都是被动引用,不会触发类初始化,其核心是虽然有引用,但需要考虑该引用是否需要对类进行初始化
  • 3个例子
  1. 子类引用父类静态变量
class SuperClass{
	static int value = 123;
}
class SubClass extends SuperClass{
}
public static void main(String[] args){
	System.out.println(SubClass.value);
}
//这里虽然存在对SubClass的引用,但访问的是父类的静态变量,那么只需要初始化SuperClass即可,不需要初始化SubClass
  1. 通过数组定义引用类
SuperClass[] sca = new SuperClass[10];
//这里只是存在定义而已,不需要对SuperClass进行初始化操作
  1. 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有两种
  1. 一种是启动类加载器(Bootstrap ClassLoader),使用C++实现,是JVM的一部分
  2. 一种是其他类加载器,在JVm外部,使用Java继承ClassLoader类实现
  • 更进一步细分及作用
  1. 启动类加载器
    加载JAVA_HOME\lib目录下的类,约定在Java中该ClassLoader的实例为null
  2. 扩展类加载器
    加载JAVA_HOME\lib\ext目录下的类
  3. 应用程序类加载器
    加载用户ClassPath上的类,一般是程序中默认的ClassLoader
  4. 自定义类加载器
    自定义从其他位置加载类

2. Parents Delegation M odel

  • 一般称为双亲委派模型,这个翻译感觉优点不准备,我认为应该翻译为父类代理加载模型
  • 流程:
    如果一个类加载器收到了类加载的请求,它首先不会自己去尝试加 载这个类,而是把这个请求委派给父类加载器去完成,每一个层次的类加载器都是如此,因此所有的 加载请求最终都应该传送到最顶层的启动类加载器中,只有当父加载器反馈自己无法完成这个加载请 求(它的搜索范围中没有找到所需的类)时,子加载器才会尝试自己去完成加载。
  • 好处
    基础类得到统一,Java 类随着它的类加载器一起具有一种带有优先级的层次关系
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值