这是我参与8月更文挑战的第14天,活动详情查看:8月更文挑战
类加载
生命周期
1.加载
将class文件load到内存中。
- 1.通过类的权限定名获取类的二进制字节流
- 2.将字节流的静态存储结构转化为方法区的运行时数据结构
- 3.内存中生成一个代表这个类的class对象,作为方法区中这个类的各种数据的访问入口
一般使用lazyLoad懒加载,只有用到这个类时,才会去加载这个类。
-
遇到new等字节码指令,如果类型没有进行过初始化,触发初始化阶段
-
使用new关键字实例化队对象
-
读取一个类的静态方法(被final修饰的已经在编译器把结果放在常量池中)
- 在编译阶段,final的变量就存储在了常量池中,当调用这个变量时是向常量池中引用,而不是去调用类
-
调用一个类的静态方法
-
-
对类进行反射,如果没有进行过初始化,触发初始化阶段
-
当初始化一个类时,如果其父类没有初始化,先初始化其父类
-
虚拟机启动时,main()方法的主类(被执行的类) 会被虚拟机先初始化
2.连接
2.1 验证:确保Class文件的字节流包含的信息符合全部约束要求,保证不会危害虚拟机的安全。
-
文件格式验证
-
元数据校验:语义分析
- 是否有父类(除了Object都有父类)
- 是否继承了不允许被继承的父类(final修饰)
- 如果这个类不是抽象类,是否实现了其父类或接口中要求实现的所有方法
-
字节码校验:确定语义时合法的,符合逻辑的
-
符号引用验证:该类是否缺少资源(依赖的类是否能找到)
2.2准备:为类变量(静态变量)分配内存并设置类变量初始值的阶段。
-
类变量设置的是初始值:即0,不是类中定义的赋值;
-
如果类字段的字段属性存在ConstantValue属性,那么这个阶段会直接设置成指定的初始值
2.3 解析:将常量池中的符号引用替换成直接引用(指针)。
- 符号引用:用一组符号来描述引用的目标,可以是字面量,只要能够无歧义的定位到目标即可
- 直接饮用:直接指向目标的指针,相对偏移量或者句柄。
3.初始化
6种主动引用,其他的都是被动应用
-
遇到new等字节码指令,如果类型没有进行过初始化,触发初始化阶段
-
使用new关键字实例化队对象
-
读取一个类的静态方法(被final修饰的已经在编译器把结果放在常量池中)
- 在编译阶段,final的变量就存储在了常量池中,当调用这个变量时是向常量池中引用,而不是去调用类
-
调用一个类的静态方法
-
-
对类进行反射,如果没有进行过初始化,触发初始化阶段
-
当初始化一个类时,如果其父类没有初始化,先初始化其父类
-
虚拟机启动时,main()方法的主类(被执行的类) 会被虚拟机先初始化
-
接口的实现类被初始化,接口需要先被初始化