《深入理解JVM》7-类加载
从Class文件到类的过程要经过
- 加载-
- 链接-
- 初始化
三个过程。根据上一节讲到的内容,java语言中分为基本类型和引用类型
基本类型是虚拟机预先定义好的,引用类型分为数组-类-接口。数组也是虚拟机直接生成的,只有类和接口对应的是字节流(操作码流)
操作码可以是class文件形式,也可以是网络上读取,也可以是机器内部生成。我们以常见的class文件为例。
加载
查找类文件,并读取字节码到内存
双亲委派机制
jvm提供了默认的三个类加载器
- 启动类加载器(boot strap classLoader),最顶级,是C++代码实现,所以获取不到,null代替。加载jre路径下 lib目录下的类
- 扩展类加载器(extension class loader),启动类加载器的子类,加载 jre下lib/ext目录下的类
- 应用类加载器 (application class loader),扩展类加载器的子类,加载应用相关的jar包等。
比如有这样一个需求,需要加载com.david.study.test.Demo.class文件。首先获取当前线程的类加载器A,然后逐级向上查找父加载器,看父加载器能否加载到Demo文件,如果可以,给内存中的Demo类取唯一符号:类加载器+类全路径名。如果父加载器加载不到类,就自己取查找文件。
注意:如果一个类被不同的加载器加载,jvm会认为两个类不是同一个类。用户也可以自定义类加载器
链接
-
验证:验证class字节码是否符合虚拟机标准(版本号,magic no等)
-
准备:准备给类的静态变量分配存储空间,构建实现虚方法动态绑定的方法表。还有调用本类的方法和属性,并不知道具体的地址,所以只能保存为目标类的名称+方法+参数+返回值这样组成的符号引用,
-
解析:把字节码当中的符号引用解析为实际地址,比如调用B对象的update方法,之前只知道update的符号地址,现在要解析为jvm中B类update的实际地址。
初始化
类中的静态字段或静态代码段,在初始化阶段给予赋值及执行,这些被集中放置在<clinit>方法中。java虚拟机在执行<clinit>方法时会加锁,保证只执行一次。
调用初始化的时机:
- 程序启动时,初始化主类
- new 命令
- 访问静态方法或静态属性
- 子类初始化会出发父类初始化
- 使用Api反射
- MethodHandle实例执行时