JVM类加载-《深入理解JVM》读书笔记
类加载(Class Loading)阶段:
1. 加载(Loading):
1)通过一个类的全限定名来获取定义此类的二进制字节流。
2)将这个字节流所代表的静态存储结构转化为方法区的运行时数据结构。
3)在Java堆中生成一个代表这个类的java.lang.Class对象,作为方法区这些数据的访问入口。
2. 连接(Linking)
验证(Verification):确保Class文件的字节流中包含的信息符合当前虚拟机要求,并且不会危害虚拟机自身的安全。
文件格式验证:验证字节流是否符合Class文件格式的规范,如魔数(Magic Number)(0XCAFEBABE)验证、主次版本号是否在当前虚拟机处理范围以内、常量池的常量中是否有不被支持的常量类型(检查常量tag标志)、指向常量的各种索引值中是否有指向不存在的常量或不符合类型的常量等。。。
元数据验证:对字节码描述的信息进行予以分析,如是否有父类、父类是否不被允许被继承(被final修饰)、若不是抽象类是否实现了抽象父类抽象方法或接口中的所有方法、类中字段方法是否与父类产生矛盾(final修饰)或不符合规则的方法重载(方法入参一致)等。。。
字节码验证:数据流和控制流分析,如保证任一时刻操作数栈的数据类型与指令代码序列都能配合工作、保证跳转指令不会跳转到方法体以外的字节码指令上、类型转换的有效性等。。。
符号引用验证:对类自身以外(常量池中的各种符号引用)的信息进行匹配性的校验,如符号引用中通过字符串描述的全限定名是否能找到对应的类、指定类中是否存在符合方法的字段描述符及简单名称所描述的方法和字段、符号引用中的类、字段和方法的访问性(private protected、public、default)是否可被当前类访问等。。。
准备(Preparation):正式为类变量(被static修饰的变量)方法区分配内存并设置类变量初始值(非类中指定值,而是数据类型的默认值,如int为0)的阶段,实例变量会随着对象实例化时一起分配到Java堆中。
解析(Resolution):是虚拟机将常量池内的符号引用替换为直接引用的过程。
*符号引用(Symbolic References):符号引用以一组符号来描述所引用的目标,符号可以是任何形式的字面量,只要使用时能无歧义地定位到目标即可。符号引用于虚拟机实现的内存布局无关,引用的目标并不一定已经加载到内存中。
*直接引用(Direct References):直接引用可以是直接指向目标的指针、相对偏移量或是一个能简介定位到目标的句柄,与虚拟机实现的内存布局相关。如果有了直接引用,那引用的目标必定已经在内存中存在。
句柄访问:Java堆中将会划分出一块内存来作为句柄池,Java栈本地变量表reference中存储的就是对象的句柄地址,而句柄地址包含了对象实例数据和类型数据各自的具体地址信息。
直接指针访问:Java栈本地变量表reference直接存储的是对象地址。
3. 初始化(Initialization):
4. 使用(Using)
5. 卸载(Unloading)
触发初始化的场景(有且只有):
1. 遇到new、getstatic、putstatic或invokestatic这4条字节码指令时,如果类没有进行过初始化,则需要先触发其初始化。代码场景:使用new关键字实例化对象的时候、读取或设置一个类的静态字段(被final修饰、已在编译期把结果放入常量池的静态字段除外)的时候,以及调用一个类的静态方法的时候
2. 使用java.lang.reflect包的方法对类进行反射调用的时候,如果类没有经过初始化,则需要先触发其初始化
3. 当初始化一个类的时候,如果发现其父类还没有进行过初始化,则需要先触发其父类的初始化
4. 当虚拟机启动时,用户需要制定一个要执行的主类(包含main()方法的那个类),虚拟机会先初始化这个主类
常见不触发初始化场景:
1. Son类继承Parent类,main方法使用Son.value等非自有方法或static变量时,不初始化Son,仅初始化方法或static变量所在的类及父类。
2. 使用某类的static final变量时,不触发初始化(加载阶段即放入常量池)。
3. 创建数组new ObjectA[10]时,不触发ObjectA的初始化,但是会触发[Lorg.fenixsoft.classloading.ObjectA的类的初始化,由虚拟机自动生成且直接继承与java.lang.Object的子类,创建动作由字节码指令newarray触发,表示ObjectA的一维数组,拥有数组中应有的属性和方法。
浙公网安备 33010602011771号