深入了解Java虚拟机(二)
2.jvm的类加载
- class文件的格式class文件是以8位字节为基础单位的二进制流,各项目数据紧密的严格排列在一起,采用的是伪结构来存储,采用的是无符号数和表两种数据类型
无符号数是基本的数据类型,以u1、u2、u4、u8来表示1、2、4、8个字节的无符号数,用来描述数字、索引引用、数量值或UTF-8构成的字符串
表是由多个无符号数或其他表作为数据项构成,习惯性的已“_info”结尾,描述有层次关系的符合结构数据
以书中的TestClass例子为准,魔数:CAFEBABE,主版本号是从45开始的,jdk是能向下兼容的,常量池计数是从1开始的,主要包含字面量(常量)和符号引用(类和接口的全限定名、字段的描述和名称、方法的描述和名称)。
以书中的例子的第一个常量池数据为参考
01表明该常量是UTF8_info即是一位,后面的两位表示长度有29个,接着就是对应的具体值,是ASCII码,可以反过头去对应的。其余的同理
可以通过javap命令来反编译输出class文件中常量池的详细信息。
后面需要理解的是,各种修饰符会在这些字节码中体现出来,另外方法体是放在code属性表中的,这里没有去做理解
- 类文件的加载过程
Java的规范是分为Java语言规范和Java虚拟机规范两部分的,Java虚拟机上是可以运行其它合乎规范的语言的
除了解析之外,其它都是按顺序开始的(没说一起结束)
类开始加载的时候没有特殊的规定,由虚拟机自行决定。
初始化的情况:1.new一个对象的时候、读取或设置静态变量的时候(被final修饰已经放到常量池的不算)、调用静态方法
2.使用反射
3.加载一个类时,如果其父类没有初始化,就要初始化父类
4.虚拟机启动调用main方法时要初始化这个类
上面的4中情况是主动引用,还要区分被动引用是不进行初始化的。1.通过子类引用父类的静态变量不会导致子类的初始化。2通过数组定义来引用,不会触发。比如user类定义一个user[]。3.常量的引用
(1)加载:1.根据classpath来获取类的二进制流,2.把二进制流里面的静态结构转换为方法区里面的数据结构。3.在Java对堆中生成这个对象。
该步是由类加载器去完成的,如果通过不同的类加载器来实现,即使是同一个类,他们的instanceof是不一致的。而且来源不一定是class文件,可以是包,网络、反射等等。而且是跟后面的交叉进行。
(2)验证:验证文件符合虚拟机的要求。主要验证四个方面,现在还没有体会,就不去深究
(3)准备:为类变量设置初始化的值,比如类变量的int是0,string是null
(4)解析:将常量池中的符号引用替换未直接引用。主要针对的是类或接口、字段、类方法、接口方法
(5)初始化:根据程序员的设定去初始化类变量和其它值,比如谈论到类的加载过程,如静态代码块,父类的加载顺序等等
类加载器
双亲委派模式:
分类:启动类加载器,是有c++实现,加载lib下面的jar包
扩展类加载器,libext
应用程序类加载器:加载classpath下面的文件
如果两个类的全限定名完全一致,那么加载器会加载第一个,那么自己定义一个类加载器来加载(是根据文件路径来做),参考:https://www.cnblogs.com/gdpuzxs/p/7044963.html