概述
虚拟机把描述类的数据从Class文件加载到内存,并对数据进行校验、转换解析和初始
化,最终形成可以被虚拟机直接使用的Java类型,这就是虚拟机的类加载机制。
类加载时机:
类从被加载到虚拟机内存中开始,到卸载出内存为止,它的整个生命周期包括:加载
(Loading)、验证(Verification)、准备(Preparation)、解析(Resolution)、初始化
(Initialization)、使用(Using)和卸载(Unloading)7个阶段。其中验证、准备、解析3个
部分统称为连接(Linking).
在加载阶段:JVM规范中没有进行约束, 这点可以交给虚拟机的具体实现来自由把握.
在初始化阶段:
JVM严格规范了有且只有以下5种情况必须立即进行初始化(初始化前,必须经过加载,验证解析,准备阶段)
1.使用new实例化对象时,读取(getstatic)和设置(putstatic)类的静态变量,静态非字面值常量时,调用静态方法(invokestatic)时
2.对内进行反射调用时, 如果类没有进行过初始化,则需要先触发其初始化
3.当初始化一个类时,如果父类没有进行初始化,首先要初始化父类.
4.启动程序所使用的main方法所在类
5.当使用1.7的动态语言支持时
以上5种称为主动引用,除此之外的引用被称为被动引用
被动引用常见情况:
- 调用父类的静态字段,只会初始化父类,不会触发子类的初始化
- 定义对象数组和集合,不会触发该类的初始化
- 类A引用类B的static final 常量不会导致类B初始化(注意静态常量必须是字面值常量,否则还是会触发B的初始化)
- 通过类名获取对象,不会触发类的初始化(System.out.println(Person.class));
- 通过Class.forName加载指定类时,如果指定参数initialize为false,也不会初始化该类
- 通过ClassLoader默认的loadClass方法,也不会触发初始化.
类加载过程:
加载流程: 加载->连接(验证->准备->解析)->初始化->使用->卸载
加载
在加载阶段 , JVM需要完成3件事情
- 查找并加载类的二进制数据(Class文件)
- 静态存储结构转化为方法区运行时数据结构:类的类信息
- Java堆生产:Class文件对应的类实例(相当于一个句柄),去访问方法区
加载.class文件的方式:
- 从本地系统中直接加载
- 通过网络下载class文件
- 将java源文件动态编译为.class文件
- …
验证
确保加载的类信息是正确的
- 文件格式验证
- 验证Class文件的标示:魔数(默认为:CAFE BABE)
- 验证Class文件的版本号
- 验证常量池(常量类型,常量类型数据结构是否正确,UTF8是否符合标准)
- Class文件的每个部分(字段表,方法表)是否正确
- 元数据验证(父类验证,继承验证,接口验证,final验证)
- 这个类是否有父类,是否实现了父类的抽象方法,是否重写了父类的final方法,是否继承了被final修饰的类等等
- 字节码验证(指令验证)
- 符号引用验证(通过符号引用是否能找到类,方法,字段)
- 通过符合引用能找到对应的类和方法
,符号引用中类、属性、方法的访问性是否能被当前类访问等等。
- 通过符合引用能找到对应的类和方法
准备
类变量:一般称为静态变量
实例变量:当对象被实例化时,实例变量就被跟着被确定
为类的静态变量(static)分配内存空间并赋予初始值(默认值) ,这些变量所使用的内存都将在方法区中进行分配.
例:static int value = 2;初始化值为0.
这时候尚未开始执行任何Java方法,而把value赋值为2的putstatic指令是程序被编译后,存放于类构造器< clinit>()方法之中,所以把value